Initial Commit

This commit is contained in:
Ben Mosley
2026-04-25 12:03:54 -05:00
commit 5d86aa000c
30 changed files with 2771 additions and 0 deletions

42
.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# Virtual environment
.venv/
env/
venv/
# Python bytecode and cache
__pycache__/
*.pyc
*.pyo
*.pyd
# Flask specific
instance/
.webassets-cache/
# Testing
.pytest_cache/
.coverage
htmlcov/
# Build artifacts
dist/
build/
*.egg-info/
# Editor specific files (optional, uncomment if applicable)
# .vscode/
# .idea/
# *.sublime-project
# *.sublime-workspace
# Database files (if using a local SQLite database)
*.db
*.sqlite
*.sqlite3
# Log files
*.log
# Sensitive files (e.g., environment variables)
.env
ga_key.json

131
About.html Normal file
View File

@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="icon" href="Logo.png" sizes="16x16">
<link rel="icon" href="Logo.png" sizes="32x32">
<link rel="icon" href="Logo.png" sizes="96x96">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="Logo.png" width="60" height="60" alt="">
<a class="navbar-brand" href="index.html">Thors Hammer Electrical</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<!-- About Us Section -->
<div class="text-center my-5">
<h1 class="text-white">About Us</h1>
</div>
<!-- Mission Statement Section -->
<div class="container my-5">
<div class="card text-white bg-dark-gray">
<div class="card-body">
<h2 class="card-title">Mission Statement</h2>
<p class="card-text">
We at Thors Hammer Electrical strive to be the best in the field. We proudly serve Levelland and the surrounding areas.</p>
<p>Remember, at Thor's Hammer Electrical, we put the power back in the panel!</p>
</div>
</div>
</div>
<!-- Company History Section -->
<div class="container my-5">
<div class="card text-white bg-dark-gray">
<div class="card-body">
<h2 class="card-title">Company History</h2>
<p class="card-text">
Thor's Hammer Electrical LLC was founded in April 2023. We have over 20 years of electrical experience. We are a small company with family values.</p>
<p> When you choose Thors Hammer Electrical for your electrical needs, you become part of the family.</p>
</div>
</div>
</div>
<div class="container my-6">
<div class="card text-white bg-dark-gray">
<div class="card-body text-center">
<h2 class="text-white">Our Team Includes:</h2>
<br>
<h3 class="text-white">Leonard Johnson:</h3>
<h4 class="text-white">Master Electrician, Company Owner and Operator</h4>
<br>
<h3 class="text-white">Makayla Johnson:</h3>
<h4 class="text-white">Electrician's Apprentice</h4>
<br>
<h3 class="text-white">Robert Johnson:</h3>
<h4 class="text-white">Electrician's Apprentice</h4>
</div>
</div>
</div>
<!-- Footer Section -->
<footer class="text-center text-white py-4">
<p>&copy; 2025 Thors Hammer Electrical LLC</p>
<button class="btn btn-light"><a href="https://www.bennyshouse.net" class="text-dark text-decoration-none">Web Design by Benny's House. Click Here to Learn More</a></button>
</footer>
</body>
</html>

54
Contact.html Normal file
View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><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=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="Logo.png" width="60" height="60" alt="">
<a class="navbar-brand" href="index.html">Thors Hammer Electrical LLC</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a>
<li class="nav-item">
<a class="nav-link" href="Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<div class="image">
<footer>2025 Thors Hammer Electrical LLC
<br>
<button class="button"><a href="https://www.bennyshouse.org"> Web Design by Benny's House. Click Here to Learn More
</a></button></footer>
</div>

97
Review.html Normal file
View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Customer Reviews</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="icon" href="Logo.png" sizes="16x16">
<link rel="icon" href="Logo.png" sizes="32x32">
<link rel="icon" href="Logo.png" sizes="96x96">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="Logo.png" width="60" height="60" alt="">
<a class="navbar-brand" href="thor.html">Thors Hammer Electrical</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<div class="wrapper text-center my-5">
<h1 class="text-white">See What Our Customers Are Saying!</h1>
<p class="text-white">Your reviews will also be featured here on our website. We appreciate your business and your feedback!</p>
</div>
<div class="text-center my-5">
<iframe class="text" src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fpermalink.php%3Fstory_fbid%3Dpfbid0hsfJ6TzR6YVhThHjB1KXX3zjHDz4bSiyjVs2HGh8mjWJPEbCJpa5V7CGGgoYgoDVl%26id%3D100008520403162&show_text=true&width=500&is_preview=true"
width="350" height="228" style="border:none;overflow:hidden"
scrolling="no" frameborder="0"
allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
<br>
<br>
<br>
<iframe class="text" src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Frojo1678%2Fposts%2Fpfbid07G3vZb4M3Cobru8PmNEKTiH4w66UKxqmoKzn63yqYpx8fqz1aDMyUCpAdU21r7Nkl&show_text=true&width=500&is_preview=true"
width="500" height="250" style="border:none;overflow:hidden"
scrolling="no" frameborder="0" allowfullscreen="true"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
</div>
<!-- Footer -->
<footer class="text-center text-white py-4">
<p>&copy; 2025 Thor's Hammer Electrical LLC</p>
<button class="btn btn-light">
<a href="https://www.bennyshouse.net" class="text-dark text-decoration-none">Web Design by Benny's House. Click Here to Learn More</a>
</button>
</footer>
</body>
</html>

122
Services.html Normal file
View File

@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Services</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Bundle JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="icon" href="Logo.png" sizes="16x16">
<link rel="icon" href="Logo.png" sizes="32x32">
<link rel="icon" href="Logo.png" sizes="96x96">
</head>
<body style="background-color: #000000; color: white;">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="Logo.png" width="60" height="60" alt="">
<a class="navbar-brand" href="index.html">Thor's Hammer Electrical</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://help.thorshammerelectrical.com">Submit Work-Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<!-- Main Content -->
<div class="text-center my-5">
<h1 class="display-3 text-white">Our Services</h1>
<br>
<h3 class="text-white">Thor's Hammer Electrical provides a diverse spread of services.</h3>
</div>
<!-- Residential Section in Card Box -->
<div class="container my-5">
<div class="card text-white bg-dark-gray">
<div class="card-body">
<h3 class="card-title">Residential</h3>
<p class="card-text">
Our residential services are the bread and butter of Thor's Hammer. We live to serve our community members and make sure their needs are met.
</p>
</div>
</div>
</div>
<!-- Commercial Section in Card Box -->
<div class="container my-5">
<div class="card text-white bg-dark-gray">
<div class="card-body">
<h3 class="card-title">Commercial</h3>
<p class="card-text">
Part of serving your community is ensuring the needs of both the people and their businesses are met. Having spent multiple years servicing commercial products, Thor's Hammer is your go-to commercial contractor!
</p>
</div>
</div>
</div>
<!-- Jobs Completed Section in Card Box -->
<div class="container my-5">
<div class="card text-white bg-dark-gray">
<div class="card-body text-center">
<h4 class="card-title">Here are some specific jobs completed by Thor's Hammer in the past.</h4>
<br>
<h5 class="text-white">(Pictures coming soon!)</h5>
<br>
<ul class="list-unstyled text-white">
<li><h5>Christmas Lights</h5></li>
<li><h5>Cat5, Cat6, and Cat6a Network Installation</h5></li>
<li><h5>Security Camera Installation</h5></li>
<li><h5>Flood Light Installation</h5></li>
</ul>
</div>
</div>
</div>
<!-- Footer -->
<footer class="text-center text-white py-4">
<p>&copy; 2025 Thor's Hammer Electrical LLC</p>
<button class="btn btn-light">
<a href="https://www.bennyshouse.net" class="text-dark text-decoration-none">Web Design by Benny's House. Click Here to Learn More</a>
</button>
</footer>
</body>
</html>

131
admin.html Normal file
View File

@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel - Work Orders</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body class="bg-dark text-white">
<!-- Admin Panel Header -->
<div class="container mt-5 text-center">
<h2>Thor's Hammer Electrical - Admin Panel</h2>
</div>
<!-- Table Container -->
<div class="container mt-4">
<table class="table table-striped table-bordered table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Job</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zipcode</th>
<th>Phone</th>
<th>Submitted At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for order in work_orders %}
<tr>
<td>{{ order[0] }}</td> <!-- ID -->
<td>{{ order[1] }}</td> <!-- Name -->
<td>{{ order[2] }}</td> <!-- Job -->
<td>{{ order[3] }}</td> <!-- Address -->
<td>{{ order[4] }}</td> <!-- City -->
<td>{{ order[5] }}</td> <!-- State -->
<td>{{ order[6] }}</td> <!-- Zipcode -->
<td>{{ order[7] }}</td> <!-- Phone -->
<td>{{ order[8] }}</td> <!-- Submitted At -->
<td>
<!-- Delete Button -->
<form action="{{ url_for('delete_order', order_id=order[0]) }}" method="POST" style="display:inline;">
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
<!-- Mark as Complete Button -->
<form action="{{ url_for('mark_complete', order_id=order[0]) }}" method="POST" style="display:inline;">
<button type="submit" class="btn btn-success btn-sm">Mark as Complete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Logout Container -->
<div class="container text-center mt-4">
<a href="{{ url_for('completed_jobs') }}" class="btn btn-info btn-lg mb-3">View Completed Jobs</a>
<!-- Logout Button -->
<form action="{{ url_for('logout') }}" method="POST" class="d-inline-block">
<button type="submit" class="btn btn-warning btn-lg mb-3">Logout</button>
</form>
</div>
<h2 class="text-center text-white">Website Analytics</h2>
<div class="row">
<div class="col-md-4">
<div class="card text-center bg-primary text-white">
<div class="card-body">
<h4>Total Visitors</h4>
<h2 id="total-visitors">Loading...</h2>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center bg-success text-white">
<div class="card-body">
<h4>Page Views</h4>
<h2 id="page-views">Loading...</h2>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-center bg-warning text-white">
<div class="card-body">
<h4>Most Visited Page</h4>
<h2 id="top-page">Loading...</h2>
</div>
</div>
</div>
</div>
<script>
async function fetchAnalytics() {
try {
let response = await fetch('/analytics');
let data = await response.json();
if (data.error) {
document.getElementById('total-visitors').textContent = "Error";
document.getElementById('page-views').textContent = "Error";
document.getElementById('top-page').textContent = "Error";
} else {
document.getElementById('total-visitors').textContent = data.total_users;
document.getElementById('page-views').textContent = data.total_pageviews;
document.getElementById('top-page').textContent = data.top_page;
}
} catch (error) {
console.error("Error fetching analytics:", error);
}
}
fetchAnalytics();
</script>
<!-- Bootstrap JS and Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>

320
app.py Normal file
View File

@@ -0,0 +1,320 @@
import os
from flask import Flask, render_template, request, redirect, url_for, session, flash, g, jsonify
from dotenv import load_dotenv
import sqlite3
from datetime import timedelta
import requests
from google.oauth2 import service_account
from googleapiclient.discovery import build
import json
import logging
load_dotenv()
app = Flask(__name__)
app.secret_key = os.getenv('FLASK_SECRET_KEY', os.urandom(24))
app.permanent_session_lifetime = timedelta(minutes=30)
app.config['SESSION_COOKIE_PATH'] = '/'
USERNAME = os.getenv('FLASK_LOGIN_USER')
PASSWORD = os.getenv('FLASK_LOGIN_PASSWORD')
DATABASE = 'work_orders.db'
PUSHOVER_API_TOKEN = os.getenv('PUSHOVER_API_TOKEN')
PUSHOVER_USER_KEY = os.getenv('PUSHOVER_USER_KEY')
GA_PROPERTY_ID = os.getenv('GA_PROPERTY_ID')
GA_CREDENTIALS = 'ga_key.json'
RECAPTCHA_SITE_KEY = os.getenv('RECAPTCHA_SITE_KEY')
RECAPTCHA_SECRET_KEY = os.getenv('RECAPTCHA_SECRET_KEY')
logging.basicConfig(
filename='ga4_debug.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
def init_db():
with app.app_context():
db = get_db()
db.execute('''CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
job TEXT NOT NULL,
address TEXT NOT NULL,
city TEXT NOT NULL,
state TEXT NOT NULL,
zipcode INTEGER NOT NULL,
phone TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);''')
db.execute('''CREATE TABLE IF NOT EXISTS completed_orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
job TEXT NOT NULL,
address TEXT NOT NULL,
city TEXT NOT NULL,
state TEXT NOT NULL,
zipcode INTEGER NOT NULL,
phone TEXT NOT NULL,
completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);''')
db.commit()
# ── Public pages ──────────────────────────────────────────────
@app.route('/')
def index():
return render_template('index.html')
@app.route('/about')
def about():
return render_template('about.html')
@app.route('/services')
def services():
return render_template('services.html')
@app.route('/reviews')
def reviews():
return render_template('reviews.html')
@app.route('/contact')
def contact():
return render_template('contact.html')
def verify_recaptcha(token):
if not RECAPTCHA_SECRET_KEY:
return True # skip check if not configured yet
try:
resp = requests.post(
'https://www.google.com/recaptcha/api/siteverify',
data={'secret': RECAPTCHA_SECRET_KEY, 'response': token},
timeout=5,
)
result = resp.json()
return result.get('success') and result.get('score', 0) >= 0.5
except Exception:
return True # fail open if Google is unreachable
@app.route('/work-order')
def work_order():
return render_template('work_order.html', recaptcha_site_key=RECAPTCHA_SITE_KEY)
# ── Work order submission ─────────────────────────────────────
@app.route('/submit', methods=['POST'])
def submit():
# Honeypot: real users never fill this field; bots usually do
if request.form.get('website'):
session['form_submitted'] = True
return redirect(url_for('success')) # silent reject — bot thinks it worked
# reCAPTCHA v3 score check
token = request.form.get('g-recaptcha-response', '')
if not verify_recaptcha(token):
session['form_submitted'] = True
return redirect(url_for('failure'))
try:
name = request.form.get('name')
job = request.form.get('job')
address = request.form.get('address')
city = request.form.get('city')
state = request.form.get('state')
zipcode = request.form.get('zipcode')
phone = request.form.get('phone')
conn = get_db()
conn.execute('''INSERT INTO orders (name, job, address, city, state, zipcode, phone)
VALUES (?, ?, ?, ?, ?, ?, ?)''',
(name, job, address, city, state, zipcode, phone))
conn.commit()
send_push_notification(name, job)
session['form_submitted'] = True
return redirect(url_for('success'))
except Exception as e:
session['form_submitted'] = True
return redirect(url_for('failure'))
@app.route('/success')
def success():
if not session.pop('form_submitted', None):
return redirect(url_for('work_order'))
return render_template('success.html')
@app.route('/failure')
def failure():
if not session.pop('form_submitted', None):
return redirect(url_for('work_order'))
return render_template('failure.html')
# ── Auth ──────────────────────────────────────────────────────
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username == USERNAME and password == PASSWORD:
session.permanent = True
session['logged_in'] = True
return redirect(url_for('admin'))
else:
flash('Invalid username or password.', 'danger')
return render_template('login.html')
@app.route('/logout', methods=['GET', 'POST'])
def logout():
session.pop('logged_in', None)
flash('You have been logged out.')
return redirect(url_for('index'))
# ── Admin ─────────────────────────────────────────────────────
@app.route('/admin')
def admin():
if not session.get('logged_in'):
return redirect(url_for('login'))
conn = get_db()
orders = conn.execute('SELECT * FROM orders').fetchall()
return render_template('admin.html', work_orders=orders)
@app.route('/delete_order/<int:order_id>', methods=['POST'])
def delete_order(order_id):
if not session.get('logged_in'):
return redirect(url_for('login'))
try:
db = get_db()
db.execute('DELETE FROM orders WHERE id = ?', (order_id,))
db.commit()
flash('Work order deleted.', 'success')
except Exception as e:
flash(f'Error deleting work order: {str(e)}', 'danger')
return redirect(url_for('admin'))
@app.route('/mark_complete/<int:order_id>', methods=['POST'])
def mark_complete(order_id):
if not session.get('logged_in'):
return redirect(url_for('login'))
try:
db = get_db()
order = db.execute('SELECT * FROM orders WHERE id = ?', (order_id,)).fetchone()
if order:
db.execute('''INSERT INTO completed_orders (name, job, address, city, state, zipcode, phone)
VALUES (?, ?, ?, ?, ?, ?, ?)''',
(order[1], order[2], order[3], order[4], order[5], order[6], order[7]))
db.execute('DELETE FROM orders WHERE id = ?', (order_id,))
db.commit()
flash('Work order marked as complete.', 'success')
else:
flash('Work order not found.', 'danger')
except Exception as e:
flash(f'Error: {str(e)}', 'danger')
return redirect(url_for('admin'))
@app.route('/completed_jobs')
def completed_jobs():
if not session.get('logged_in'):
return redirect(url_for('login'))
db = get_db()
jobs = db.execute('SELECT * FROM completed_orders').fetchall()
return render_template('completed_jobs.html', completed_jobs=jobs)
# ── Analytics ─────────────────────────────────────────────────
def get_ga4_data():
try:
credentials = service_account.Credentials.from_service_account_file(
GA_CREDENTIALS, scopes=["https://www.googleapis.com/auth/analytics.readonly"]
)
analytics = build("analyticsdata", "v1beta", credentials=credentials)
response = analytics.properties().runReport(
property=f"properties/{int(GA_PROPERTY_ID)}",
body={
"dateRanges": [{"startDate": "7daysAgo", "endDate": "yesterday"}],
"metrics": [{"name": "totalUsers"}, {"name": "screenPageViews"}],
"dimensions": [{"name": "pagePath"}],
"limit": 1,
},
).execute()
logging.debug("Full GA4 API Response: %s", json.dumps(response, indent=2))
rows = response.get("rows", [])
if not rows:
return {"total_users": "0", "total_pageviews": "0", "top_page": "N/A"}
top_page = rows[0]["dimensionValues"][0]["value"]
metric_values = rows[0]["metricValues"]
total_users = metric_values[0]["value"] if len(metric_values) > 0 else "0"
total_pageviews = metric_values[1]["value"] if len(metric_values) > 1 else "0"
return {"total_users": total_users, "total_pageviews": total_pageviews, "top_page": top_page}
except Exception as e:
logging.error("Google Analytics API Error: %s", str(e))
return {"total_users": "0", "total_pageviews": "0", "top_page": "N/A"}
@app.route('/analytics')
def analytics():
if not session.get('logged_in'):
return redirect(url_for('login'))
return jsonify(get_ga4_data())
# ── Notifications ─────────────────────────────────────────────
def send_push_notification(name, job):
if not PUSHOVER_API_TOKEN or not PUSHOVER_USER_KEY:
return
message = f"New Work Order:\nName: {name}\nJob: {job}"
data = {
"token": PUSHOVER_API_TOKEN,
"user": PUSHOVER_USER_KEY,
"message": message,
}
response = requests.post("https://api.pushover.net/1/messages.json", data=data)
if response.status_code != 200:
print(f"Push notification failed: {response.status_code} - {response.text}")
if __name__ == '__main__':
init_db()
app.run(debug=True)

61
completed_jobs.html Normal file
View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel - Completed Jobs</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body class="bg-dark text-white">
<!-- Admin Panel Header -->
<div class="container mt-5 text-center">
<h2>Thor's Hammer Electrical - Completed Jobs</h2>
</div>
<!-- Table Container -->
<div class="container mt-4">
<table class="table table-striped table-bordered table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Job</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zipcode</th>
<th>Phone</th>
<th>Completed At</th>
</tr>
</thead>
<tbody>
{% for job in completed_jobs %}
<tr>
<td>{{ job[0] }}</td> <!-- ID -->
<td>{{ job[1] }}</td> <!-- Name -->
<td>{{ job[2] }}</td> <!-- Job -->
<td>{{ job[3] }}</td> <!-- Address -->
<td>{{ job[4] }}</td> <!-- City -->
<td>{{ job[5] }}</td> <!-- State -->
<td>{{ job[6] }}</td> <!-- Zipcode -->
<td>{{ job[7] }}</td> <!-- Phone -->
<td>{{ job[8] }}</td> <!-- Completed At -->
</tr>
{% endfor %}
</tbody>
</table>
<div class="back-container">
<a href="{{ url_for('admin') }}">
<button class="btn btn-success btn-sm">
Back to Admin Panel</button>
</a>
</div>
</body>
</html>

62
dash.html Normal file
View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Website Analytics Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1 class="text-center text-white">Website Analytics Dashboard</h1>
<div class="row">
<!-- Total Visitors -->
<div class="col-md-4">
<div class="card text-center bg-primary text-white">
<div class="card-body">
<h4>Total Visitors</h4>
<h2 id="total-visitors">Loading...</h2>
</div>
</div>
</div>
<!-- Page Views -->
<div class="col-md-4">
<div class="card text-center bg-success text-white">
<div class="card-body">
<h4>Page Views</h4>
<h2 id="page-views">Loading...</h2>
</div>
</div>
</div>
<!-- Most Visited Page -->
<div class="col-md-4">
<div class="card text-center bg-warning text-white">
<div class="card-body">
<h4>Most Visited Page</h4>
<h2 id="top-page">Loading...</h2>
</div>
</div>
</div>
</div>
</div>
<!-- Google Analytics API Script -->
<script>
async function fetchAnalytics() {
// Simulated Data (Replace with API Fetch)
setTimeout(() => {
document.getElementById('total-visitors').textContent = "1,234";
document.getElementById('page-views').textContent = "5,678";
document.getElementById('top-page').textContent = "/home";
}, 2000);
}
fetchAnalytics();
</script>
</body>
</html>

74
failure.html Normal file
View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Work Order Form</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="icon" href="{{ url_for('static', filename='Logo.png') }}">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="{{ url_for('static', filename='Logo.png') }}" width="60" height="60">
<a class="navbar-brand" href="https://thorshammerelectrical.com/index.html">Thor's Hammer Electrical</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="https://thorshammerelectrical.com/About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://thorshammerelectrical.com/Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://thorshammerelectrical.com/Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<div class="d-flex min-vh-100 align-items-center justify-content-center">
<div class="d-flex flex-column align-items-center text-center">
<div class="p-2">
<h1>Something went wrong. Please try again!</h1>
</div>
<br>
<div class="p-2">
<button class="btn btn-danger">
<a href="{{ url_for('index') }}" class="text-white text-decoration-none">Back to Form</a>
</button>
</div>
</div>
</div>

93
index.html Normal file
View File

@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="en">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thor's Hammer Electrical LLC</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<link rel="icon" href="Logo.png" sizes="16x16">
<link rel="icon" href="Logo.png" sizes="32x32">
<link rel="icon" href="Logo.png" sizes="96x96">
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container d-flex align-items-center justify-content-between">
<!-- Logo and Brand Name -->
<a class="navbar-brand d-flex align-items-center" href="index.html">
<img src="Logo.png" width="50" height="50" class="me-2" alt="Logo">
Thor's Hammer Electrical
</a>
<!-- Navbar Toggler Button -->
<button class="navbar-toggler ms-auto" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Navbar Links -->
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav text-end">
<li class="nav-item"><a class="nav-link" href="About.html">About</a></li>
<li class="nav-item"><a class="nav-link" href="Services.html">Services</a></li>
<li class="nav-item"><a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a></li>
<li class="nav-item"><a class="nav-link" href="Review.html">Reviews</a></li>
</ul>
</div>
</div>
</nav>
<div class="wrapper text-center my-5">
<h1 class="text-white">Welcome to the Thor's Hammer Official Website!</h1>
</div>
<div class="image text-center">
<img class="licenseimage img-fluid" src="License.jpg" alt="License Image">
</div>
<div class="wrapper text-center my-5">
<h5 class="text-white">
Thor's Hammer Electrical LLC is a local electrical services company based in Levelland, Texas.
<br><br><br>
Owned and operated by Leonard Johnson, Thor's Hammer Electrical LLC looks to serve customers far and wide, big and small.
</h5>
</div>
<div class="wrapper text-center my-5">
<button class="btn btn-primary m-2"><a href="https://www.help.thorshammerelectrical.com." class="text-white text-decoration-none">Need Work Done?</a></button>
<button class="btn btn-primary m-2"><a href="Services.html" class="text-white text-decoration-none">See What We Do</a></button>
<button class="btn btn-primary m-2"><a href="About.html" class="text-white text-decoration-none">Company Background</a></button>
</div>
<footer class="text-center text-white py-4">
<div class="image">
<p>&copy; 2025 Thor's Hammer Electrical LLC</p>
<button class="btn btn-light"><a href="https://www.bennyshouse.net" class="text-dark text-decoration-none">Web Design by Benny's House. Click Here to Learn More</a></button>
</div>
</footer>
</body>
</html>

67
login.html Normal file
View File

@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<title>Login</title>
<style>
body {
background: linear-gradient(to right, #667eea, #764ba2);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.login-container {
background: white;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-align: center;
max-width: 400px;
width: 100%;
}
.logo-placeholder {
width: 80px;
height: 80px;
background: #ddd;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto 1rem;
font-weight: bold;
color: #666;
}
.logo {
width: 100px; /* Adjust size as needed */
height: auto;
display: block;
margin: 0 auto 1rem; /* Centers the logo */
}
</style>
</head>
<body>
<div class="login-container">
<img src="{{ url_for('static', filename='Logo.png') }}" alt="Logo" class="logo">
<h2 class="mb-3">Login</h2>
<form action="/login" method="POST">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Login</button>
</form>
</div>
</body>
</html>

6
requirements.txt Normal file
View File

@@ -0,0 +1,6 @@
flask
python-dotenv
requests
google-auth
google-api-python-client
gunicorn

BIN
static/images/License.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/images/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 KiB

11
styles.css Normal file
View File

@@ -0,0 +1,11 @@
.bg-dark-gray {
background-color: #333333; /* Darker Gray */
}
body {
background-color: #000000; /* Black background for body */
}

74
success.html Normal file
View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Work Order Form</title>
<link rel="stylesheet" href="styles.css" type="text/css" />
<!-- Cyborg Bootstrap Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.0/dist/cyborg/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<link rel="icon" href="{{ url_for('static', filename='Logo.png') }}">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Oswald:wght@200..700&display=swap" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<img src="{{ url_for('static', filename='Logo.png') }}" width="60" height="60">
<a class="navbar-brand" href="https://thorshammerelectrical.com/index.html">Thor's Hammer Electrical</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item active">
<a class="nav-link" href="https://thorshammerelectrical.com/About.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://thorshammerelectrical.com/Services.html">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://www.help.thorshammerelectrical.com">Submit Work-Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://thorshammerelectrical.com/Review.html">Reviews</a>
</li>
</ul>
</div>
</nav>
<div class="d-flex min-vh-100 align-items-center justify-content-center">
<div class="d-flex flex-column align-items-center text-center">
<div class="p-2">
<h1>Success! Thank you for choosing Thor's Hammer</h1>
</div>
<br>
<div class="p-2">
<button class="btn btn-danger">
<a href="https://thorshammerelectrical.com" class="text-white text-decoration-none">Back to Home</a>
</button>
</div>
</div>
</div>

135
templates/about.html Normal file
View File

@@ -0,0 +1,135 @@
{% extends "base.html" %}
{% block title %}About — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<!-- ── PAGE HEADER ─────────────────────────────────────────── -->
<section class="relative py-24 px-4 text-center overflow-hidden grid-bg">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(250,204,21,0.06) 0%, transparent 70%);"></div>
<div class="relative z-10">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Our Story</p>
<h1 class="font-oswald text-5xl md:text-6xl font-bold text-white tracking-wide">ABOUT US</h1>
</div>
</section>
<!-- ── MISSION ─────────────────────────────────────────────── -->
<section class="py-16 px-4">
<div class="max-w-4xl mx-auto">
<div class="card-hover rounded-2xl p-8 md:p-10 border border-yellow-400/15"
style="background-color: #111111;">
<div class="flex items-center gap-4 mb-6">
<div class="w-12 h-12 rounded-xl bg-yellow-400/10 flex items-center justify-center flex-shrink-0">
<i class="fa-solid fa-bullseye text-yellow-400 text-xl"></i>
</div>
<h2 class="font-oswald text-2xl md:text-3xl font-bold text-white">Mission Statement</h2>
</div>
<p class="text-gray-300 leading-relaxed text-lg mb-4">
We at Thor's Hammer Electrical strive to be the best in the field. We proudly serve
Levelland and the surrounding areas with dedication, integrity, and craftsmanship.
</p>
<p class="text-yellow-400 font-oswald text-lg font-medium tracking-wide">
"At Thor's Hammer Electrical, we put the power back in the panel!"
</p>
</div>
</div>
</section>
<!-- ── HISTORY ─────────────────────────────────────────────── -->
<section class="py-8 px-4">
<div class="max-w-4xl mx-auto">
<div class="card-hover rounded-2xl p-8 md:p-10 border border-white/8"
style="background-color: #111111;">
<div class="flex items-center gap-4 mb-6">
<div class="w-12 h-12 rounded-xl bg-yellow-400/10 flex items-center justify-center flex-shrink-0">
<i class="fa-solid fa-book-open text-yellow-400 text-xl"></i>
</div>
<h2 class="font-oswald text-2xl md:text-3xl font-bold text-white">Company History</h2>
</div>
<p class="text-gray-300 leading-relaxed mb-4">
Thor's Hammer Electrical LLC was founded in April 2023. We bring over <strong class="text-white">20 years of
electrical experience</strong> to every project, operating as a small company with big family values.
</p>
<p class="text-gray-300 leading-relaxed">
When you choose Thor's Hammer Electrical for your electrical needs,
you don't just get a contractor — you become part of the family.
</p>
</div>
</div>
</section>
<!-- ── TEAM ─────────────────────────────────────────────────── -->
<section class="py-16 px-4" style="background-color: #0d0d0d;">
<div class="max-w-4xl mx-auto text-center">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">The Crew</p>
<h2 class="font-oswald text-3xl md:text-4xl font-bold text-white mb-12">Our Team</h2>
<div class="grid md:grid-cols-3 gap-6">
<!-- Leonard -->
<div class="card-hover rounded-2xl p-8 border border-yellow-400/20"
style="background: linear-gradient(135deg, #141414, #111111);">
<div class="w-16 h-16 rounded-full bg-yellow-400/15 flex items-center justify-center mx-auto mb-5
ring-2 ring-yellow-400/30">
<i class="fa-solid fa-helmet-safety text-yellow-400 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-1">Leonard Johnson</h3>
<p class="text-yellow-400 text-sm font-medium tracking-wide mb-3">Master Electrician</p>
<p class="text-gray-500 text-xs leading-relaxed">Company Owner &amp; Operator<br/>20+ Years Experience</p>
</div>
<!-- Makayla -->
<div class="card-hover rounded-2xl p-8 border border-white/8"
style="background-color: #111111;">
<div class="w-16 h-16 rounded-full bg-yellow-400/10 flex items-center justify-center mx-auto mb-5
ring-2 ring-white/10">
<i class="fa-solid fa-screwdriver-wrench text-yellow-400/70 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-1">Makayla Johnson</h3>
<p class="text-yellow-400/70 text-sm font-medium tracking-wide mb-3">Electrician's Apprentice</p>
<p class="text-gray-500 text-xs leading-relaxed">Team Member</p>
</div>
<!-- Robert -->
<div class="card-hover rounded-2xl p-8 border border-white/8"
style="background-color: #111111;">
<div class="w-16 h-16 rounded-full bg-yellow-400/10 flex items-center justify-center mx-auto mb-5
ring-2 ring-white/10">
<i class="fa-solid fa-screwdriver-wrench text-yellow-400/70 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-1">Robert Johnson</h3>
<p class="text-yellow-400/70 text-sm font-medium tracking-wide mb-3">Electrician's Apprentice</p>
<p class="text-gray-500 text-xs leading-relaxed">Team Member</p>
</div>
<!-- Benjamin Mosley -->
<div class="card-hover rounded-2xl p-8 border border-white/8"
style="background-color: #111111;">
<div class="w-16 h-16 rounded-full bg-yellow-400/10 flex items-center justify-center mx-auto mb-5
ring-2 ring-white/10">
<i class="fa-solid fa-laptop-code text-yellow-400/70 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-1">Benjamin Mosley</h3>
<p class="text-yellow-400/70 text-sm font-medium tracking-wide mb-3">IT &amp; Web</p>
<p class="text-gray-500 text-xs leading-relaxed">
<a href="https://www.bennyshouse.net" target="_blank" rel="noopener noreferrer"
class="hover:text-yellow-400 transition-colors">Benny's House</a>
</p>
</div>
</div>
</div>
</section>
<!-- ── CTA ──────────────────────────────────────────────────── -->
<section class="py-16 px-4 text-center">
<h2 class="font-oswald text-3xl font-bold text-white mb-4">Need Electrical Work Done?</h2>
<p class="text-gray-400 mb-8">Submit a work order and we'll be in touch.</p>
<a href="{{ url_for('work_order') }}"
class="inline-block px-10 py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-base rounded-lg hover:bg-yellow-300 transition-all glow-yellow">
<i class="fa-solid fa-bolt mr-2"></i> SUBMIT WORK ORDER
</a>
</section>
{% endblock %}

171
templates/admin.html Normal file
View File

@@ -0,0 +1,171 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel — Thor's Hammer Electrical</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<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=Oswald:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="icon" href="{{ url_for('static', filename='images/Logo.png') }}">
<style>
body { font-family: system-ui, sans-serif; background-color: #080808; }
.font-oswald { font-family: 'Oswald', sans-serif; }
</style>
</head>
<body class="text-white min-h-screen" style="background-color: #080808;">
<!-- Header bar -->
<header style="background-color: #0f0f0f; border-bottom: 1px solid rgba(250,204,21,0.15);" class="sticky top-0 z-10">
<div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between gap-4 flex-wrap">
<div class="flex items-center gap-3">
<img src="{{ url_for('static', filename='images/Logo.png') }}" alt="Logo"
class="h-8 w-8 rounded-full ring-1 ring-yellow-400/30">
<h1 class="font-oswald text-lg font-bold tracking-wide">
<span class="text-yellow-400">Thor's Hammer</span>
<span class="text-gray-400 font-light"> · Admin Panel</span>
</h1>
</div>
<div class="flex items-center gap-3">
<a href="{{ url_for('completed_jobs') }}"
class="px-4 py-2 text-sm rounded-lg border border-white/10 text-gray-300
hover:bg-white/5 hover:text-white transition-all font-medium">
Completed Jobs
</a>
<form action="{{ url_for('logout') }}" method="POST" class="inline">
<button type="submit"
class="px-4 py-2 text-sm rounded-lg bg-red-900/50 border border-red-500/30
text-red-300 hover:bg-red-900 transition-all font-medium">
Logout
</button>
</form>
</div>
</div>
</header>
<main class="max-w-7xl mx-auto px-4 py-8">
<!-- Flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="mb-6 space-y-2">
{% for category, message in messages %}
<div class="px-4 py-3 rounded-lg border text-sm
{% if category == 'success' %}bg-green-950 border-green-500/50 text-green-300
{% elif category == 'danger' %}bg-red-950 border-red-500/50 text-red-300
{% else %}bg-yellow-950 border-yellow-500/50 text-yellow-300{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<!-- Analytics Cards -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-8">
<div class="rounded-xl p-5 border border-blue-500/20" style="background-color: #111111;">
<p class="text-gray-400 text-xs uppercase tracking-widest mb-2">Total Visitors (7d)</p>
<p id="total-visitors" class="font-oswald text-3xl font-bold text-blue-400"></p>
</div>
<div class="rounded-xl p-5 border border-green-500/20" style="background-color: #111111;">
<p class="text-gray-400 text-xs uppercase tracking-widest mb-2">Page Views (7d)</p>
<p id="page-views" class="font-oswald text-3xl font-bold text-green-400"></p>
</div>
<div class="rounded-xl p-5 border border-yellow-500/20" style="background-color: #111111;">
<p class="text-gray-400 text-xs uppercase tracking-widest mb-2">Top Page (7d)</p>
<p id="top-page" class="font-oswald text-3xl font-bold text-yellow-400 truncate"></p>
</div>
</div>
<!-- Work Orders -->
<div class="rounded-2xl border border-white/8 overflow-hidden" style="background-color: #111111;">
<div class="px-6 py-5 border-b border-white/8 flex items-center justify-between">
<div>
<h2 class="font-oswald text-xl font-bold text-white">Active Work Orders</h2>
<p class="text-gray-500 text-xs mt-0.5">{{ work_orders | length }} order(s) pending</p>
</div>
</div>
{% if work_orders %}
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr style="background-color: #161616; border-bottom: 1px solid rgba(255,255,255,0.06);">
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">ID</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Name</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Job</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Address</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Phone</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Submitted</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Actions</th>
</tr>
</thead>
<tbody>
{% for order in work_orders %}
<tr class="border-b border-white/5 hover:bg-white/2 transition-colors">
<td class="px-4 py-4 text-gray-500 text-xs font-mono">#{{ order[0] }}</td>
<td class="px-4 py-4 text-white font-medium">{{ order[1] }}</td>
<td class="px-4 py-4 text-gray-300 max-w-xs">
<span class="block truncate" title="{{ order[2] }}">{{ order[2] }}</span>
</td>
<td class="px-4 py-4 text-gray-400 text-xs">
{{ order[3] }}, {{ order[4] }}, {{ order[5] }} {{ order[6] }}
</td>
<td class="px-4 py-4 text-gray-400 text-xs">{{ order[7] }}</td>
<td class="px-4 py-4 text-gray-500 text-xs">{{ order[8] }}</td>
<td class="px-4 py-4">
<div class="flex items-center gap-2">
<form action="{{ url_for('mark_complete', order_id=order[0]) }}" method="POST">
<button type="submit"
class="px-3 py-1.5 text-xs rounded-md bg-green-900/60 border border-green-500/30
text-green-300 hover:bg-green-900 transition-all font-medium whitespace-nowrap">
✓ Complete
</button>
</form>
<form action="{{ url_for('delete_order', order_id=order[0]) }}" method="POST"
onsubmit="return confirm('Delete this work order?');">
<button type="submit"
class="px-3 py-1.5 text-xs rounded-md bg-red-900/60 border border-red-500/30
text-red-300 hover:bg-red-900 transition-all font-medium">
Delete
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="py-16 text-center">
<i class="fa-solid fa-clipboard-list text-gray-600 text-4xl mb-3 block"></i>
<p class="text-gray-400 font-medium">No active work orders</p>
<p class="text-gray-600 text-sm mt-1">New orders will appear here</p>
</div>
{% endif %}
</div>
</main>
<script>
async function fetchAnalytics() {
try {
const res = await fetch('/analytics');
const data = await res.json();
document.getElementById('total-visitors').textContent = data.error ? 'Error' : data.total_users;
document.getElementById('page-views').textContent = data.error ? 'Error' : data.total_pageviews;
document.getElementById('top-page').textContent = data.error ? 'Error' : data.top_page;
} catch (e) {
['total-visitors','page-views','top-page'].forEach(id => {
document.getElementById(id).textContent = 'N/A';
});
}
}
fetchAnalytics();
</script>
</body>
</html>

180
templates/base.html Normal file
View File

@@ -0,0 +1,180 @@
<!DOCTYPE html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Thor's Hammer Electrical LLC{% endblock %}</title>
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-ZJ4YKQNNG1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZJ4YKQNNG1');
</script>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome 6 Free -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Google Fonts: Oswald -->
<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=Oswald:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Favicon -->
<link rel="icon" href="{{ url_for('static', filename='images/Logo.png') }}">
<style>
body { font-family: 'Inter', system-ui, sans-serif; background-color: #080808; }
.font-oswald { font-family: 'Oswald', sans-serif; }
.glow-yellow { box-shadow: 0 0 20px rgba(250, 204, 21, 0.4), 0 0 60px rgba(250, 204, 21, 0.15); }
.glow-yellow-sm { box-shadow: 0 0 12px rgba(250, 204, 21, 0.35); }
.card-hover { transition: transform 0.2s ease, box-shadow 0.2s ease; }
.card-hover:hover { transform: translateY(-3px); box-shadow: 0 8px 30px rgba(250, 204, 21, 0.15); }
.grid-bg {
background-image:
linear-gradient(rgba(250,204,21,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(250,204,21,0.03) 1px, transparent 1px);
background-size: 60px 60px;
}
.honeypot { position: absolute; left: -9999px; top: -9999px; }
.text-gradient {
background: linear-gradient(135deg, #facc15, #f59e0b);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.nav-link {
position: relative;
padding: 6px 14px;
color: #d1d5db;
font-weight: 500;
transition: color 0.2s;
border-radius: 6px;
}
.nav-link:hover { color: #facc15; background: rgba(255,255,255,0.04); }
.nav-link.active { color: #facc15; }
</style>
{% block extra_head %}{% endblock %}
</head>
<body class="text-white antialiased" style="background-color: #080808;">
<!-- Navbar -->
<nav style="background-color: #0f0f0f; border-bottom: 1px solid rgba(250,204,21,0.15);" class="sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between h-16">
<!-- Brand -->
<a href="{{ url_for('index') }}" class="flex items-center gap-3 group flex-shrink-0">
<img src="{{ url_for('static', filename='images/Logo.png') }}" alt="Logo" class="h-10 w-10 rounded-full ring-2 ring-yellow-400/30 group-hover:ring-yellow-400/70 transition-all">
<span class="font-oswald text-lg font-bold text-white group-hover:text-yellow-400 transition-colors tracking-wide hidden sm:block">
Thor's Hammer Electrical
</span>
</a>
<!-- Desktop Links -->
<div class="hidden md:flex items-center gap-1">
<a href="{{ url_for('index') }}" class="nav-link">Home</a>
<a href="{{ url_for('about') }}" class="nav-link">About</a>
<a href="{{ url_for('services') }}" class="nav-link">Services</a>
<a href="{{ url_for('reviews') }}" class="nav-link">Reviews</a>
<a href="{{ url_for('contact') }}" class="nav-link">Contact</a>
<a href="{{ url_for('work_order') }}"
class="ml-3 px-5 py-2 bg-yellow-400 text-black font-oswald font-bold tracking-wider text-sm rounded-lg hover:bg-yellow-300 transition-all glow-yellow-sm">
<i class="fa-solid fa-bolt mr-1"></i> WORK ORDER
</a>
</div>
<!-- Mobile Toggle -->
<button id="mobile-btn" type="button" aria-label="Toggle navigation" class="md:hidden p-2 rounded-lg text-gray-400 hover:text-yellow-400 hover:bg-white/5 transition-all">
<svg id="icon-open" class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
<svg id="icon-close" class="h-6 w-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="hidden md:hidden pb-4 border-t border-white/5 mt-2 pt-3">
<div class="flex flex-col gap-1">
<a href="{{ url_for('index') }}" class="nav-link">Home</a>
<a href="{{ url_for('about') }}" class="nav-link">About</a>
<a href="{{ url_for('services') }}" class="nav-link">Services</a>
<a href="{{ url_for('reviews') }}" class="nav-link">Reviews</a>
<a href="{{ url_for('contact') }}" class="nav-link">Contact</a>
<a href="{{ url_for('work_order') }}"
class="mt-2 px-5 py-3 bg-yellow-400 text-black font-oswald font-bold tracking-wider text-sm rounded-lg text-center hover:bg-yellow-300 transition-all">
<i class="fa-solid fa-bolt mr-1"></i> SUBMIT WORK ORDER
</a>
</div>
</div>
</div>
</nav>
<!-- Flash Messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="max-w-7xl mx-auto px-4 pt-4 space-y-2">
{% for category, message in messages %}
<div class="px-4 py-3 rounded-lg border text-sm font-medium
{% if category == 'success' %}bg-green-950 border-green-500/50 text-green-300
{% elif category == 'danger' %}bg-red-950 border-red-500/50 text-red-300
{% else %}bg-yellow-950 border-yellow-500/50 text-yellow-300{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<!-- Main Content -->
{% block content %}{% endblock %}
<!-- Footer -->
<footer style="background-color: #0f0f0f; border-top: 1px solid rgba(250,204,21,0.12);" class="mt-24 py-12">
<div class="max-w-7xl mx-auto px-4 text-center">
<div class="flex justify-center mb-5">
<img src="{{ url_for('static', filename='images/Logo.png') }}" alt="Logo"
class="h-14 w-14 rounded-full ring-2 ring-yellow-400/30">
</div>
<p class="font-oswald text-2xl text-yellow-400 tracking-widest mb-1">THOR'S HAMMER ELECTRICAL LLC</p>
<p class="text-gray-600 text-sm mb-2">Levelland, Texas</p>
<p class="text-gray-600 text-sm mb-6">&copy; 2025 Thor's Hammer Electrical LLC. All rights reserved.</p>
<div class="flex justify-center gap-4 flex-wrap">
<a href="{{ url_for('index') }}" class="text-gray-500 hover:text-yellow-400 text-sm transition-colors">Home</a>
<a href="{{ url_for('about') }}" class="text-gray-500 hover:text-yellow-400 text-sm transition-colors">About</a>
<a href="{{ url_for('services') }}" class="text-gray-500 hover:text-yellow-400 text-sm transition-colors">Services</a>
<a href="{{ url_for('reviews') }}" class="text-gray-500 hover:text-yellow-400 text-sm transition-colors">Reviews</a>
<a href="{{ url_for('contact') }}" class="text-gray-500 hover:text-yellow-400 text-sm transition-colors">Contact</a>
<a href="{{ url_for('work_order') }}" class="text-yellow-500 hover:text-yellow-300 text-sm transition-colors font-medium">Submit Work Order</a>
</div>
<div class="mt-8 pt-6 border-t border-white/5">
<a href="https://www.bennyshouse.net" target="_blank" rel="noopener noreferrer"
class="inline-flex items-center gap-2 px-4 py-2 bg-white/5 hover:bg-white/10 rounded-lg text-gray-500 hover:text-gray-300 text-xs transition-all">
Web Design by Benny's House
</a>
</div>
</div>
</footer>
<script>
const btn = document.getElementById('mobile-btn');
const menu = document.getElementById('mobile-menu');
const iconOpen = document.getElementById('icon-open');
const iconClose = document.getElementById('icon-close');
btn.addEventListener('click', () => {
menu.classList.toggle('hidden');
iconOpen.classList.toggle('hidden');
iconClose.classList.toggle('hidden');
});
</script>
{% block extra_scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Completed Jobs — Thor's Hammer Electrical</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<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=Oswald:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="icon" href="{{ url_for('static', filename='images/Logo.png') }}">
<style>
body { font-family: system-ui, sans-serif; background-color: #080808; }
.font-oswald { font-family: 'Oswald', sans-serif; }
</style>
</head>
<body class="text-white min-h-screen" style="background-color: #080808;">
<!-- Header bar -->
<header style="background-color: #0f0f0f; border-bottom: 1px solid rgba(250,204,21,0.15);" class="sticky top-0 z-10">
<div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between gap-4 flex-wrap">
<div class="flex items-center gap-3">
<img src="{{ url_for('static', filename='images/Logo.png') }}" alt="Logo"
class="h-8 w-8 rounded-full ring-1 ring-yellow-400/30">
<h1 class="font-oswald text-lg font-bold tracking-wide">
<span class="text-yellow-400">Thor's Hammer</span>
<span class="text-gray-400 font-light"> · Completed Jobs</span>
</h1>
</div>
<div class="flex items-center gap-3">
<a href="{{ url_for('admin') }}"
class="px-4 py-2 text-sm rounded-lg border border-white/10 text-gray-300
hover:bg-white/5 hover:text-white transition-all font-medium">
← Active Orders
</a>
<form action="{{ url_for('logout') }}" method="POST" class="inline">
<button type="submit"
class="px-4 py-2 text-sm rounded-lg bg-red-900/50 border border-red-500/30
text-red-300 hover:bg-red-900 transition-all font-medium">
Logout
</button>
</form>
</div>
</div>
</header>
<main class="max-w-7xl mx-auto px-4 py-8">
<!-- Flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="mb-6 space-y-2">
{% for category, message in messages %}
<div class="px-4 py-3 rounded-lg border text-sm
{% if category == 'success' %}bg-green-950 border-green-500/50 text-green-300
{% elif category == 'danger' %}bg-red-950 border-red-500/50 text-red-300
{% else %}bg-yellow-950 border-yellow-500/50 text-yellow-300{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<!-- Completed Jobs Table -->
<div class="rounded-2xl border border-white/8 overflow-hidden" style="background-color: #111111;">
<div class="px-6 py-5 border-b border-white/8">
<h2 class="font-oswald text-xl font-bold text-white">Completed Jobs</h2>
<p class="text-gray-500 text-xs mt-0.5">{{ completed_jobs | length }} job(s) completed</p>
</div>
{% if completed_jobs %}
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr style="background-color: #161616; border-bottom: 1px solid rgba(255,255,255,0.06);">
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">ID</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Name</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Job</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Address</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Phone</th>
<th class="px-4 py-3 text-left text-xs text-gray-500 uppercase tracking-wider font-medium">Completed</th>
</tr>
</thead>
<tbody>
{% for job in completed_jobs %}
<tr class="border-b border-white/5 hover:bg-white/2 transition-colors">
<td class="px-4 py-4 text-gray-500 text-xs font-mono">#{{ job[0] }}</td>
<td class="px-4 py-4 text-white font-medium">{{ job[1] }}</td>
<td class="px-4 py-4 text-gray-300 max-w-xs">
<span class="block truncate" title="{{ job[2] }}">{{ job[2] }}</span>
</td>
<td class="px-4 py-4 text-gray-400 text-xs">
{{ job[3] }}, {{ job[4] }}, {{ job[5] }} {{ job[6] }}
</td>
<td class="px-4 py-4 text-gray-400 text-xs">{{ job[7] }}</td>
<td class="px-4 py-4 text-green-400 text-xs">
<span class="flex items-center gap-1">
<span class="w-1.5 h-1.5 rounded-full bg-green-400 inline-block"></span>
{{ job[8] }}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="py-16 text-center">
<i class="fa-solid fa-circle-check text-gray-600 text-4xl mb-3 block"></i>
<p class="text-gray-400 font-medium">No completed jobs yet</p>
<p class="text-gray-600 text-sm mt-1">Jobs marked as complete will appear here</p>
</div>
{% endif %}
</div>
</main>
</body>
</html>

64
templates/contact.html Normal file
View File

@@ -0,0 +1,64 @@
{% extends "base.html" %}
{% block title %}Contact — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<!-- ── PAGE HEADER ─────────────────────────────────────────── -->
<section class="relative py-24 px-4 text-center overflow-hidden grid-bg">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(250,204,21,0.06) 0%, transparent 70%);"></div>
<div class="relative z-10">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Get In Touch</p>
<h1 class="font-oswald text-5xl md:text-6xl font-bold text-white tracking-wide mb-4">CONTACT US</h1>
<p class="text-gray-400 max-w-xl mx-auto">
Have a question or need to reach us directly? Fill out the form below and we'll get back to you.
</p>
</div>
</section>
<!-- ── CONTACT INFO + FORM ─────────────────────────────────── -->
<section class="py-16 px-4">
<div class="max-w-5xl mx-auto">
<div class="grid md:grid-cols-3 gap-8 mb-12">
<div class="card-hover rounded-xl p-6 border border-white/8 text-center" style="background-color: #111111;">
<i class="fa-solid fa-location-dot text-yellow-400 text-2xl mb-3 block"></i>
<h3 class="font-oswald font-semibold text-white mb-1">Location</h3>
<p class="text-gray-400 text-sm">Levelland, Texas</p>
</div>
<div class="card-hover rounded-xl p-6 border border-white/8 text-center" style="background-color: #111111;">
<i class="fa-solid fa-bolt text-yellow-400 text-2xl mb-3 block"></i>
<h3 class="font-oswald font-semibold text-white mb-1">Work Orders</h3>
<p class="text-gray-400 text-sm">
<a href="{{ url_for('work_order') }}" class="text-yellow-400 hover:text-yellow-300 transition-colors">
Submit here →
</a>
</p>
</div>
<div class="card-hover rounded-xl p-6 border border-white/8 text-center" style="background-color: #111111;">
<i class="fa-solid fa-clock text-yellow-400 text-2xl mb-3 block"></i>
<h3 class="font-oswald font-semibold text-white mb-1">Response Time</h3>
<p class="text-gray-400 text-sm">We respond promptly</p>
</div>
</div>
<!-- Work Order CTA -->
<div class="rounded-2xl p-10 text-center border border-yellow-400/15"
style="background: linear-gradient(135deg, #141414, #111111);">
<i class="fa-solid fa-bolt text-yellow-400 text-4xl mb-5 block"></i>
<h2 class="font-oswald text-2xl font-bold text-white mb-3">Ready to Get Work Done?</h2>
<p class="text-gray-400 mb-8 max-w-md mx-auto">
Submit a work order and we'll be notified right away. Fill out your name, job description,
and address and we'll follow up with you shortly.
</p>
<a href="{{ url_for('work_order') }}"
class="inline-block px-10 py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-base rounded-lg hover:bg-yellow-300 transition-all glow-yellow">
SUBMIT WORK ORDER
</a>
</div>
</div>
</section>
{% endblock %}

46
templates/failure.html Normal file
View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block title %}Something Went Wrong — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<section class="min-h-[80vh] flex items-center justify-center px-4">
<div class="max-w-lg w-full text-center">
<div class="rounded-2xl p-10 border border-red-500/20"
style="background: linear-gradient(135deg, #141414, #111111);">
<!-- Icon -->
<div class="w-20 h-20 rounded-full bg-red-500/10 flex items-center justify-center mx-auto mb-6
ring-2 ring-red-500/30">
<i class="fa-solid fa-triangle-exclamation text-red-400 text-3xl"></i>
</div>
<h1 class="font-oswald text-3xl md:text-4xl font-bold text-white mb-3">
Something Went Wrong
</h1>
<p class="text-red-400 font-oswald text-lg tracking-wide mb-4">
We couldn't process your work order
</p>
<p class="text-gray-400 text-sm leading-relaxed mb-8">
We're sorry for the inconvenience. Please try submitting your order again.
If the issue persists, feel free to contact us directly.
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center">
<a href="{{ url_for('work_order') }}"
class="px-8 py-3 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-sm rounded-lg hover:bg-yellow-300 transition-all">
TRY AGAIN
</a>
<a href="{{ url_for('contact') }}"
class="px-8 py-3 border border-white/15 text-gray-300 font-oswald font-bold tracking-widest
text-sm rounded-lg hover:bg-white/5 transition-all">
CONTACT US
</a>
</div>
</div>
</div>
</section>
{% endblock %}

213
templates/index.html Normal file
View File

@@ -0,0 +1,213 @@
{% extends "base.html" %}
{% block title %}Thor's Hammer Electrical LLC — Levelland, TX{% endblock %}
{% block content %}
<!-- ── HERO ────────────────────────────────────────────────── -->
<section class="relative min-h-screen flex items-center justify-center overflow-hidden grid-bg">
<!-- Radial glow behind content -->
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 80% 60% at 50% 40%, rgba(250,204,21,0.07) 0%, transparent 70%);"></div>
<!-- Bottom fade -->
<div class="absolute bottom-0 left-0 right-0 h-32"
style="background: linear-gradient(to bottom, transparent, #080808);"></div>
<div class="relative z-10 text-center px-4 max-w-4xl mx-auto">
<!-- Lightning bolt icon -->
<div class="mb-6 flex justify-center">
<i class="fa-solid fa-bolt text-yellow-400"
style="font-size: 5rem; filter: drop-shadow(0 0 24px rgba(250,204,21,0.75));"></i>
</div>
<h1 class="font-oswald font-bold tracking-widest text-white mb-2"
style="font-size: clamp(2.5rem, 8vw, 6rem); line-height: 1.05;">
THOR'S HAMMER
</h1>
<h2 class="font-oswald font-light tracking-[0.25em] mb-6 text-gradient"
style="font-size: clamp(1.2rem, 4vw, 2.5rem);">
ELECTRICAL LLC
</h2>
<p class="text-gray-400 text-lg md:text-xl mb-4 max-w-2xl mx-auto leading-relaxed">
Levelland, Texas's Premier Electrical Contractor
</p>
<p class="text-gray-500 text-sm md:text-base mb-10 max-w-xl mx-auto">
Residential &amp; Commercial · Licensed &amp; Insured · 20+ Years Experience
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a href="{{ url_for('work_order') }}"
class="px-8 py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest text-base rounded-lg
hover:bg-yellow-300 transition-all glow-yellow">
<i class="fa-solid fa-bolt mr-2"></i> SUBMIT WORK ORDER
</a>
<a href="{{ url_for('services') }}"
class="px-8 py-4 border-2 border-yellow-400/60 text-yellow-400 font-oswald font-bold tracking-widest text-base rounded-lg
hover:bg-yellow-400 hover:text-black hover:border-yellow-400 transition-all">
VIEW SERVICES
</a>
</div>
</div>
<!-- Scroll indicator -->
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 animate-bounce text-yellow-400/40">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</div>
</section>
<!-- ── STATS BAR ───────────────────────────────────────────── -->
<section style="background-color: #111111; border-top: 1px solid rgba(250,204,21,0.12); border-bottom: 1px solid rgba(250,204,21,0.12);">
<div class="max-w-5xl mx-auto px-4 py-8">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-6 text-center">
<div class="group">
<p class="font-oswald text-4xl font-bold text-yellow-400 group-hover:text-yellow-300 transition-colors">20+</p>
<p class="text-gray-400 text-sm tracking-widest uppercase mt-1">Years of Experience</p>
</div>
<div class="group border-t sm:border-t-0 sm:border-x border-white/10 pt-6 sm:pt-0">
<p class="font-oswald text-4xl font-bold text-yellow-400 group-hover:text-yellow-300 transition-colors">100%</p>
<p class="text-gray-400 text-sm tracking-widest uppercase mt-1">Licensed &amp; Insured</p>
</div>
<div class="group border-t sm:border-t-0 pt-6 sm:pt-0">
<p class="font-oswald text-4xl font-bold text-yellow-400 group-hover:text-yellow-300 transition-colors">Local</p>
<p class="text-gray-400 text-sm tracking-widest uppercase mt-1">Levelland, Texas</p>
</div>
</div>
</div>
</section>
<!-- ── LICENSE ─────────────────────────────────────────────── -->
<section class="py-20 px-4">
<div class="max-w-3xl mx-auto text-center">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-4">Certified &amp; Trusted</p>
<h2 class="font-oswald text-3xl md:text-4xl font-bold text-white mb-8">Our License</h2>
<div class="inline-block rounded-xl overflow-hidden ring-2 ring-yellow-400/30 hover:ring-yellow-400/60 transition-all glow-yellow-sm">
<img src="{{ url_for('static', filename='images/License.jpg') }}"
alt="Electrical License" class="max-w-full h-auto max-h-96 object-contain">
</div>
</div>
</section>
<!-- ── ABOUT SNIPPET ───────────────────────────────────────── -->
<section class="py-20 px-4" style="background-color: #0d0d0d;">
<div class="max-w-5xl mx-auto">
<div class="grid md:grid-cols-2 gap-12 items-center">
<div>
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Who We Are</p>
<h2 class="font-oswald text-4xl md:text-5xl font-bold text-white mb-6 leading-tight">
Powering Levelland<br/>
<span class="text-gradient">Since 2023</span>
</h2>
<p class="text-gray-400 leading-relaxed mb-4">
Thor's Hammer Electrical LLC is a locally owned and operated electrical services company
based in Levelland, Texas. Owned by Master Electrician Leonard Johnson, we bring
over 20 years of hands-on experience to every job.
</p>
<p class="text-gray-400 leading-relaxed mb-8">
We put the power back in the panel — and we treat every customer like family.
</p>
<a href="{{ url_for('about') }}"
class="inline-flex items-center gap-2 px-6 py-3 border border-yellow-400/50 text-yellow-400
font-oswald font-bold tracking-wider text-sm rounded-lg hover:bg-yellow-400 hover:text-black transition-all">
LEARN MORE
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/>
</svg>
</a>
</div>
<!-- Feature tiles -->
<div class="grid grid-cols-2 gap-4">
{% set features = [
('fa-bolt', 'Residential', 'Home wiring, panels, lighting & more'),
('fa-industry', 'Commercial', 'Business & industrial electrical work'),
('fa-shield-halved', 'Licensed', 'Fully licensed Master Electrician'),
('fa-handshake', 'Family Values', 'You become part of our family'),
] %}
{% for icon, title, desc in features %}
<div class="card-hover rounded-xl p-5 border border-white/8"
style="background-color: #161616;">
<i class="fa-solid {{ icon }} text-yellow-400 text-xl mb-3 block"></i>
<h3 class="font-oswald font-semibold text-white text-sm tracking-wide mb-1">{{ title }}</h3>
<p class="text-gray-500 text-xs leading-relaxed">{{ desc }}</p>
</div>
{% endfor %}
</div>
</div>
</div>
</section>
<!-- ── SERVICES PREVIEW ────────────────────────────────────── -->
<section class="py-20 px-4">
<div class="max-w-5xl mx-auto text-center">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">What We Do</p>
<h2 class="font-oswald text-4xl md:text-5xl font-bold text-white mb-4">Our Services</h2>
<p class="text-gray-400 mb-12 max-w-xl mx-auto">
From residential homes to commercial businesses, Thor's Hammer has you covered.
</p>
<div class="grid md:grid-cols-2 gap-6 mb-10">
<div class="card-hover rounded-2xl p-8 border border-white/8 text-left"
style="background-color: #111111;">
<div class="w-12 h-12 rounded-xl bg-yellow-400/10 flex items-center justify-center mb-5">
<i class="fa-solid fa-house text-yellow-400 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-3">Residential</h3>
<p class="text-gray-400 text-sm leading-relaxed">
Our residential services are the bread and butter of Thor's Hammer. We live to serve
our community members and ensure every home is safe and powered.
</p>
</div>
<div class="card-hover rounded-2xl p-8 border border-white/8 text-left"
style="background-color: #111111;">
<div class="w-12 h-12 rounded-xl bg-yellow-400/10 flex items-center justify-center mb-5">
<i class="fa-solid fa-building text-yellow-400 text-2xl"></i>
</div>
<h3 class="font-oswald text-xl font-bold text-white mb-3">Commercial</h3>
<p class="text-gray-400 text-sm leading-relaxed">
Having spent years servicing commercial properties, Thor's Hammer is your go-to
commercial electrical contractor for businesses of all sizes.
</p>
</div>
</div>
<a href="{{ url_for('services') }}"
class="inline-flex items-center gap-2 px-8 py-3 border-2 border-yellow-400/60 text-yellow-400
font-oswald font-bold tracking-wider text-sm rounded-lg hover:bg-yellow-400 hover:text-black transition-all">
SEE ALL SERVICES
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/>
</svg>
</a>
</div>
</section>
<!-- ── CALL TO ACTION ──────────────────────────────────────── -->
<section class="py-20 px-4" style="background-color: #0d0d0d;">
<div class="max-w-3xl mx-auto text-center">
<div class="rounded-2xl p-10 md:p-14 border border-yellow-400/20 relative overflow-hidden"
style="background: linear-gradient(135deg, #111111, #0f0f0f);">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 60% 50% at 50% 50%, rgba(250,204,21,0.05) 0%, transparent 70%);"></div>
<div class="relative z-10">
<i class="fa-solid fa-bolt text-yellow-400 text-5xl mb-6 block"></i>
<h2 class="font-oswald text-3xl md:text-4xl font-bold text-white mb-4">
Ready to Get Started?
</h2>
<p class="text-gray-400 mb-8 text-lg">
Submit your work order today and we'll get back to you promptly.
</p>
<a href="{{ url_for('work_order') }}"
class="inline-block px-10 py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-base rounded-lg hover:bg-yellow-300 transition-all glow-yellow">
SUBMIT WORK ORDER
</a>
</div>
</div>
</div>
</section>
{% endblock %}

89
templates/login.html Normal file
View File

@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login — Thor's Hammer Electrical</title>
<script src="https://cdn.tailwindcss.com"></script>
<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=Oswald:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="icon" href="{{ url_for('static', filename='images/Logo.png') }}">
<style>
body { font-family: system-ui, sans-serif; background-color: #080808; }
.font-oswald { font-family: 'Oswald', sans-serif; }
.glow-yellow { box-shadow: 0 0 20px rgba(250,204,21,0.35); }
.grid-bg {
background-image:
linear-gradient(rgba(250,204,21,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(250,204,21,0.025) 1px, transparent 1px);
background-size: 60px 60px;
}
</style>
</head>
<body class="min-h-screen flex items-center justify-center grid-bg text-white" style="background-color: #080808;">
<div class="w-full max-w-sm px-4">
<!-- Logo -->
<div class="text-center mb-8">
<img src="{{ url_for('static', filename='images/Logo.png') }}" alt="Logo"
class="h-16 w-16 rounded-full mx-auto mb-4 ring-2 ring-yellow-400/40">
<h1 class="font-oswald text-2xl font-bold text-white tracking-widest">ADMIN LOGIN</h1>
<p class="text-gray-500 text-xs mt-1">Thor's Hammer Electrical</p>
</div>
<!-- Flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="mb-4 space-y-2">
{% for category, message in messages %}
<div class="px-4 py-3 rounded-lg border text-sm
{% if category == 'danger' %}bg-red-950 border-red-500/50 text-red-300
{% else %}bg-yellow-950 border-yellow-500/50 text-yellow-300{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<!-- Card -->
<div class="rounded-2xl border border-white/8 overflow-hidden" style="background-color: #111111;">
<form action="/login" method="POST" class="px-8 py-8 space-y-5">
<div>
<label for="username" class="block text-sm font-medium text-gray-300 mb-2">Username</label>
<input type="text" id="username" name="username" required autofocus
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-300 mb-2">Password</label>
<input type="password" id="password" name="password" required
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<button type="submit"
class="w-full py-3 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-sm rounded-lg hover:bg-yellow-300 active:scale-[0.99] transition-all glow-yellow">
LOGIN
</button>
</form>
</div>
<p class="text-center mt-6">
<a href="{{ url_for('index') }}" class="text-gray-600 hover:text-yellow-400 text-xs transition-colors">
← Back to Website
</a>
</p>
</div>
</body>
</html>

75
templates/reviews.html Normal file
View File

@@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block title %}Reviews — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<!-- ── PAGE HEADER ─────────────────────────────────────────── -->
<section class="relative py-24 px-4 text-center overflow-hidden grid-bg">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(250,204,21,0.06) 0%, transparent 70%);"></div>
<div class="relative z-10">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Happy Customers</p>
<h1 class="font-oswald text-5xl md:text-6xl font-bold text-white tracking-wide mb-4">REVIEWS</h1>
<p class="text-gray-400 max-w-xl mx-auto">
See what our customers are saying. Your reviews may be featured here too!
</p>
</div>
</section>
<!-- ── REVIEWS ─────────────────────────────────────────────── -->
<section class="py-16 px-4">
<div class="max-w-3xl mx-auto">
<div class="text-center mb-10">
<p class="text-gray-400 text-sm">
We appreciate your business and your feedback. Reviews from our Facebook page are shown below.
</p>
</div>
<!-- Facebook embeds wrapped in styled containers -->
<div class="space-y-8">
<div class="card-hover rounded-2xl p-6 border border-white/8 flex justify-center"
style="background-color: #111111;">
<iframe
title="Customer review from Facebook"
src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fpermalink.php%3Fstory_fbid%3Dpfbid0hsfJ6TzR6YVhThHjB1KXX3zjHDz4bSiyjVs2HGh8mjWJPEbCJpa5V7CGGgoYgoDVl%26id%3D100008520403162&show_text=true&width=500&is_preview=true"
width="100%" height="250"
style="border:none; overflow:hidden; max-width:500px; border-radius: 8px;"
scrolling="no" frameborder="0" allowfullscreen="true"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
</div>
<div class="card-hover rounded-2xl p-6 border border-white/8 flex justify-center"
style="background-color: #111111;">
<iframe
title="Customer review from Facebook"
src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Frojo1678%2Fposts%2Fpfbid07G3vZb4M3Cobru8PmNEKTiH4w66UKxqmoKzn63yqYpx8fqz1aDMyUCpAdU21r7Nkl&show_text=true&width=500&is_preview=true"
width="100%" height="280"
style="border:none; overflow:hidden; max-width:500px; border-radius: 8px;"
scrolling="no" frameborder="0" allowfullscreen="true"
allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
</div>
</div>
<!-- Leave a review nudge -->
<div class="mt-12 text-center p-8 rounded-2xl border border-yellow-400/15"
style="background-color: #111111;">
<i class="fa-solid fa-star text-yellow-400 text-2xl mb-3 block"></i>
<h3 class="font-oswald text-xl font-bold text-white mb-2">Leave Us a Review!</h3>
<p class="text-gray-400 text-sm mb-5">
Your feedback means the world to us. Leave a review on Facebook and we may feature it here.
</p>
<a href="https://www.facebook.com" target="_blank" rel="noopener noreferrer"
class="inline-block px-6 py-3 bg-yellow-400 text-black font-oswald font-bold tracking-wider
text-sm rounded-lg hover:bg-yellow-300 transition-all">
LEAVE A REVIEW
</a>
</div>
</div>
</section>
{% endblock %}

110
templates/services.html Normal file
View File

@@ -0,0 +1,110 @@
{% extends "base.html" %}
{% block title %}Services — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<!-- ── PAGE HEADER ─────────────────────────────────────────── -->
<section class="relative py-24 px-4 text-center overflow-hidden grid-bg">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(250,204,21,0.06) 0%, transparent 70%);"></div>
<div class="relative z-10">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">What We Offer</p>
<h1 class="font-oswald text-5xl md:text-6xl font-bold text-white tracking-wide mb-4">OUR SERVICES</h1>
<p class="text-gray-400 max-w-xl mx-auto text-lg">
Thor's Hammer Electrical provides a diverse range of services for homes and businesses.
</p>
</div>
</section>
<!-- ── MAIN SERVICES ───────────────────────────────────────── -->
<section class="py-16 px-4">
<div class="max-w-5xl mx-auto grid md:grid-cols-2 gap-8">
<!-- Residential -->
<div class="card-hover rounded-2xl p-8 md:p-10 border border-yellow-400/20"
style="background: linear-gradient(135deg, #141414, #111111);">
<div class="w-14 h-14 rounded-xl bg-yellow-400/10 flex items-center justify-center mb-6
ring-1 ring-yellow-400/30">
<i class="fa-solid fa-house text-yellow-400 text-2xl"></i>
</div>
<h2 class="font-oswald text-2xl md:text-3xl font-bold text-white mb-4">Residential</h2>
<p class="text-gray-400 leading-relaxed mb-6">
Our residential services are the bread and butter of Thor's Hammer. We live to serve
our community members and make sure their homes are safe, functional, and up to code.
</p>
<ul class="space-y-2">
{% for item in ['Panel upgrades & repairs', 'Outlet & switch installation', 'Lighting installation', 'Wiring & rewiring', 'Ceiling fan installation', 'Christmas light installation', 'Security camera installation'] %}
<li class="flex items-center gap-3 text-gray-300 text-sm">
<span class="text-yellow-400 flex-shrink-0"></span> {{ item }}
</li>
{% endfor %}
</ul>
</div>
<!-- Commercial -->
<div class="card-hover rounded-2xl p-8 md:p-10 border border-white/8"
style="background-color: #111111;">
<div class="w-14 h-14 rounded-xl bg-yellow-400/10 flex items-center justify-center mb-6
ring-1 ring-white/10">
<i class="fa-solid fa-building text-yellow-400 text-2xl"></i>
</div>
<h2 class="font-oswald text-2xl md:text-3xl font-bold text-white mb-4">Commercial</h2>
<p class="text-gray-400 leading-relaxed mb-6">
Part of serving your community is ensuring the needs of businesses are met. Having spent
multiple years servicing commercial properties, Thor's Hammer is your go-to commercial
electrical contractor.
</p>
<ul class="space-y-2">
{% for item in ['Commercial wiring', 'Electrical inspections', 'Lighting systems', 'Power distribution', 'Code compliance'] %}
<li class="flex items-center gap-3 text-gray-300 text-sm">
<span class="text-yellow-400 flex-shrink-0"></span> {{ item }}
</li>
{% endfor %}
</ul>
</div>
</div>
</section>
<!-- ── COMPLETED JOBS ──────────────────────────────────────── -->
<section class="py-16 px-4" style="background-color: #0d0d0d;">
<div class="max-w-5xl mx-auto">
<div class="text-center mb-12">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Our Work</p>
<h2 class="font-oswald text-3xl md:text-4xl font-bold text-white">Jobs We've Completed</h2>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
{% set jobs = [
('fa-tree', 'Christmas Lights', 'Seasonal decorative lighting installation'),
('fa-network-wired', 'Network Cabling', 'Cat5, Cat6, and Cat6a Network Installation'),
('fa-camera', 'Security Cameras', 'Security camera system installation'),
('fa-lightbulb', 'Flood Lights', 'Outdoor flood light installation'),
] %}
{% for icon, title, desc in jobs %}
<div class="card-hover rounded-xl p-6 border border-white/8 text-center"
style="background-color: #111111;">
<i class="fa-solid {{ icon }} text-yellow-400 text-2xl mb-3 block"></i>
<h3 class="font-oswald font-semibold text-white text-sm tracking-wide mb-2">{{ title }}</h3>
<p class="text-gray-500 text-xs leading-relaxed">{{ desc }}</p>
</div>
{% endfor %}
</div>
<div class="text-center mt-10 p-6 rounded-xl border border-dashed border-yellow-400/20">
<p class="text-gray-500 text-sm">📸 Project photos coming soon!</p>
</div>
</div>
</section>
<!-- ── CTA ──────────────────────────────────────────────────── -->
<section class="py-16 px-4 text-center">
<h2 class="font-oswald text-3xl font-bold text-white mb-4">Need Any of These Services?</h2>
<p class="text-gray-400 mb-8">We serve Levelland, TX and surrounding areas. Submit a work order today.</p>
<a href="{{ url_for('work_order') }}"
class="inline-block px-10 py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-base rounded-lg hover:bg-yellow-300 transition-all glow-yellow">
<i class="fa-solid fa-bolt mr-2"></i> SUBMIT WORK ORDER
</a>
</section>
{% endblock %}

46
templates/success.html Normal file
View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block title %}Order Submitted — Thor's Hammer Electrical LLC{% endblock %}
{% block content %}
<section class="min-h-[80vh] flex items-center justify-center px-4">
<div class="max-w-lg w-full text-center">
<div class="rounded-2xl p-10 border border-yellow-400/20"
style="background: linear-gradient(135deg, #141414, #111111);">
<!-- Icon -->
<div class="w-20 h-20 rounded-full bg-yellow-400/10 flex items-center justify-center mx-auto mb-6
ring-2 ring-yellow-400/40">
<i class="fa-solid fa-bolt text-yellow-400 text-3xl"></i>
</div>
<h1 class="font-oswald text-3xl md:text-4xl font-bold text-white mb-3">
Order Submitted!
</h1>
<p class="text-yellow-400 font-oswald text-lg tracking-wide mb-4">
Thank you for choosing Thor's Hammer Electrical
</p>
<p class="text-gray-400 text-sm leading-relaxed mb-8">
We've received your work order and will be in touch shortly.
We appreciate your business!
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center">
<a href="{{ url_for('index') }}"
class="px-8 py-3 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-sm rounded-lg hover:bg-yellow-300 transition-all">
BACK TO HOME
</a>
<a href="{{ url_for('services') }}"
class="px-8 py-3 border border-white/15 text-gray-300 font-oswald font-bold tracking-widest
text-sm rounded-lg hover:bg-white/5 transition-all">
OUR SERVICES
</a>
</div>
</div>
</div>
</section>
{% endblock %}

171
templates/work_order.html Normal file
View File

@@ -0,0 +1,171 @@
{% extends "base.html" %}
{% block title %}Submit Work Order — Thor's Hammer Electrical LLC{% endblock %}
{% if recaptcha_site_key %}
{% block extra_head %}
<script src="https://www.google.com/recaptcha/api.js?render={{ recaptcha_site_key }}"></script>
{% endblock %}
{% endif %}
{% block content %}
<!-- ── PAGE HEADER ─────────────────────────────────────────── -->
<section class="relative py-20 px-4 text-center overflow-hidden grid-bg">
<div class="absolute inset-0 pointer-events-none"
style="background: radial-gradient(ellipse 70% 60% at 50% 50%, rgba(250,204,21,0.06) 0%, transparent 70%);"></div>
<div class="relative z-10">
<p class="font-oswald text-sm text-yellow-400 tracking-[0.3em] uppercase mb-3">Get Service</p>
<h1 class="font-oswald text-5xl md:text-6xl font-bold text-white tracking-wide mb-4">WORK ORDER</h1>
<p class="text-gray-400 max-w-lg mx-auto">
Fill out the form below and we'll be notified immediately. We'll follow up with you shortly.
</p>
</div>
</section>
<!-- ── FORM ────────────────────────────────────────────────── -->
<section class="py-16 px-4">
<div class="max-w-2xl mx-auto">
<div class="rounded-2xl border border-white/8 overflow-hidden" style="background-color: #111111;">
<!-- Form header -->
<div class="px-8 py-6 border-b border-white/8"
style="background: linear-gradient(135deg, #161616, #131313);">
<div class="flex items-center gap-3">
<i class="fa-solid fa-bolt text-yellow-400 text-xl"></i>
<div>
<h2 class="font-oswald text-xl font-bold text-white">New Work Order</h2>
<p class="text-gray-500 text-xs mt-0.5">All fields are required</p>
</div>
</div>
</div>
<!-- Form body -->
<form id="work-order-form" action="{{ url_for('submit') }}" method="POST" class="px-8 py-8 space-y-6">
<!-- Honeypot: hidden from real users, bots fill it and get silently rejected -->
<div class="honeypot" aria-hidden="true">
<input type="text" name="website" id="website" tabindex="-1" autocomplete="off">
</div>
<!-- reCAPTCHA v3 token (populated by JS before submit) -->
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response">
<!-- Name -->
<div>
<label for="name" class="block text-sm font-medium text-gray-300 mb-2">
Full Name
</label>
<input type="text" id="name" name="name" required
placeholder="John Smith"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<!-- Job Description -->
<div>
<label for="job" class="block text-sm font-medium text-gray-300 mb-2">
Job Description
</label>
<textarea id="job" name="job" required rows="3"
placeholder="Describe the electrical work needed..."
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all resize-none"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);"></textarea>
</div>
<!-- Address -->
<div>
<label for="address" class="block text-sm font-medium text-gray-300 mb-2">
Street Address
</label>
<input type="text" id="address" name="address" required
placeholder="123 Main St"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<!-- City / State / Zip -->
<div class="grid grid-cols-2 sm:grid-cols-3 gap-4">
<div class="col-span-2 sm:col-span-1">
<label for="city" class="block text-sm font-medium text-gray-300 mb-2">City</label>
<input type="text" id="city" name="city" required
placeholder="Levelland"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<div>
<label for="state" class="block text-sm font-medium text-gray-300 mb-2">State</label>
<input type="text" id="state" name="state" required
placeholder="TX" maxlength="2"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all uppercase"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<div>
<label for="zipcode" class="block text-sm font-medium text-gray-300 mb-2">ZIP</label>
<input type="text" id="zipcode" name="zipcode" required
placeholder="79336" maxlength="5" pattern="\d{5}"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
</div>
<!-- Phone -->
<div>
<label for="phone" class="block text-sm font-medium text-gray-300 mb-2">
Phone Number
</label>
<input type="tel" id="phone" name="phone" required
placeholder="(806) 555-0123"
class="w-full px-4 py-3 rounded-lg border text-white text-sm placeholder-gray-600
focus:outline-none focus:ring-2 focus:ring-yellow-400/50 focus:border-yellow-400/50
transition-all"
style="background-color: #1a1a1a; border-color: rgba(255,255,255,0.1);">
</div>
<!-- Submit -->
<div class="pt-2">
<button type="submit"
class="w-full py-4 bg-yellow-400 text-black font-oswald font-bold tracking-widest
text-base rounded-lg hover:bg-yellow-300 active:scale-[0.99]
transition-all glow-yellow">
<i class="fa-solid fa-bolt mr-2"></i> SUBMIT WORK ORDER
</button>
<p class="text-center text-gray-600 text-xs mt-3">
We'll receive a notification immediately and follow up with you.
</p>
</div>
</form>
</div>
</div>
</section>
{% endblock %}
{% if recaptcha_site_key %}
{% block extra_scripts %}
<script>
document.getElementById('work-order-form').addEventListener('submit', function(e) {
e.preventDefault();
const form = this;
grecaptcha.ready(function() {
grecaptcha.execute('{{ recaptcha_site_key }}', { action: 'submit' }).then(function(token) {
document.getElementById('g-recaptcha-response').value = token;
form.submit();
});
});
});
</script>
{% endblock %}
{% endif %}

5
wsgi.py Normal file
View File

@@ -0,0 +1,5 @@
from app import app
if __name__ == '__main__':
app.run()