TenderRadar website with CSS fixes
- Fixed footer logo contrast (dark → white on dark background) - Fixed avatar sizing and gradient contrasts - Fixed testimonial layout with flexbox - Fixed signup form contrast and LastPass icon overlap - Added responsive company logos section - Fixed FAQ accordion CSS - All CSS improvements for WCAG compliance
This commit is contained in:
967
profile.html
Normal file
967
profile.html
Normal file
@@ -0,0 +1,967 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- Primary Meta Tags -->
|
||||
<title>Profile & Settings | TenderRadar</title>
|
||||
<meta name="title" content="Profile & Settings | TenderRadar">
|
||||
<meta name="description" content="Manage your TenderRadar profile and alert preferences.">
|
||||
<meta name="keywords" content="tender profile, alert settings">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href="https://tenderradar.co.uk/profile.html">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://tenderradar.co.uk/profile.html">
|
||||
<meta property="og:title" content="TenderRadar Profile">
|
||||
<meta property="og:description" content="Manage your profile and preferences.">
|
||||
<meta property="og:image" content="https://tenderradar.co.uk/og-image.png">
|
||||
<meta property="og:locale" content="en_GB">
|
||||
<meta property="og:site_name" content="TenderRadar">
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:url" content="https://tenderradar.co.uk/profile.html">
|
||||
<meta name="twitter:title" content="TenderRadar Profile">
|
||||
<meta name="twitter:description" content="Manage your profile and preferences.">
|
||||
<meta name="twitter:image" content="https://tenderradar.co.uk/twitter-card.png">
|
||||
|
||||
<!-- Preconnect for Performance -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon">
|
||||
|
||||
<!-- Stylesheet -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style>
|
||||
/* Profile Page Specific Styles */
|
||||
.profile-container {
|
||||
display: grid;
|
||||
grid-template-columns: 250px 1fr;
|
||||
gap: 2rem;
|
||||
min-height: calc(100vh - 72px);
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.profile-sidebar {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 1.5rem;
|
||||
height: fit-content;
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 90px;
|
||||
}
|
||||
|
||||
.profile-sidebar h3 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-light);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.profile-sidebar-menu {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.profile-sidebar-menu li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.profile-sidebar-menu a {
|
||||
display: block;
|
||||
padding: 0.75rem 1rem;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
border-radius: 0.5rem;
|
||||
transition: all 0.2s;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.profile-sidebar-menu a:hover,
|
||||
.profile-sidebar-menu a.active {
|
||||
background: var(--bg-alt);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.profile-main {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
padding: 2.5rem;
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.profile-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-section.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.profile-section h2 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.profile-section-desc {
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.form-section:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.form-section h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select,
|
||||
.form-group textarea {
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5rem;
|
||||
font-family: inherit;
|
||||
font-size: 0.9375rem;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.form-group input:focus,
|
||||
.form-group select:focus,
|
||||
.form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.1);
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* Tag Input */
|
||||
.tag-input-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5rem;
|
||||
min-height: 44px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tag-input-container.focused {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.1);
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 0.375rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tag button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tag-input {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
/* Multi-select */
|
||||
.multi-select {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--primary);
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.btn-save,
|
||||
.btn-cancel {
|
||||
padding: 0.875rem 2rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-save:hover:not(:disabled) {
|
||||
background: var(--primary-dark);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.btn-save:disabled {
|
||||
background: var(--text-light);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background: var(--bg-alt);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
/* Status Messages */
|
||||
.alert {
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
color: #7f1d1d;
|
||||
}
|
||||
|
||||
.form-help {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-light);
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.profile-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.profile-sidebar {
|
||||
position: static;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
}
|
||||
|
||||
.profile-sidebar-menu {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
}
|
||||
|
||||
.profile-main {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.btn-save,
|
||||
.btn-cancel {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profile-section h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Header/Navigation -->
|
||||
<header class="header" role="banner">
|
||||
<nav class="nav container" role="navigation" aria-label="Main navigation">
|
||||
<div class="nav-brand">
|
||||
<img src="/logo.png" alt="TenderRadar" class="logo-icon">
|
||||
</div>
|
||||
<ul class="nav-menu">
|
||||
<li><a href="/">Dashboard</a></li>
|
||||
<li><a href="/alerts.html">Alerts</a></li>
|
||||
<li><a href="/profile.html" class="active-nav">Profile</a></li>
|
||||
<li><button id="logoutBtn" class="btn btn-outline btn-sm">Logout</button></li>
|
||||
</ul>
|
||||
<button class="mobile-toggle" aria-label="Toggle navigation menu" aria-expanded="false">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="container">
|
||||
<div class="profile-container">
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside class="profile-sidebar">
|
||||
<h3>Settings</h3>
|
||||
<ul class="profile-sidebar-menu">
|
||||
<li><a href="#company" class="sidebar-link active" data-section="company">Company Profile</a></li>
|
||||
<li><a href="#alerts" class="sidebar-link" data-section="alerts">Alert Preferences</a></li>
|
||||
<li><a href="#account" class="sidebar-link" data-section="account">Account</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="profile-main">
|
||||
<!-- Status Messages -->
|
||||
<div id="successMessage" class="alert alert-success"></div>
|
||||
<div id="errorMessage" class="alert alert-error"></div>
|
||||
|
||||
<!-- Company Profile Section -->
|
||||
<section id="company" class="profile-section active">
|
||||
<h2>Company Profile</h2>
|
||||
<p class="profile-section-desc">Tell us about your company so we can find the best tender matches for you.</p>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Basic Information</h3>
|
||||
<div class="form-group">
|
||||
<label for="companyName">Company Name *</label>
|
||||
<input type="text" id="companyName" name="companyName" placeholder="Enter your company name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="industry">Industry/Sector *</label>
|
||||
<select id="industry" name="industry" required>
|
||||
<option value="">Select an industry</option>
|
||||
<option value="construction">Construction</option>
|
||||
<option value="consulting">Consulting</option>
|
||||
<option value="it">IT & Software</option>
|
||||
<option value="professional_services">Professional Services</option>
|
||||
<option value="manufacturing">Manufacturing</option>
|
||||
<option value="logistics">Logistics & Transport</option>
|
||||
<option value="healthcare">Healthcare</option>
|
||||
<option value="engineering">Engineering</option>
|
||||
<option value="facilities">Facilities Management</option>
|
||||
<option value="training">Training & Education</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="companySize">Company Size *</label>
|
||||
<select id="companySize" name="companySize" required>
|
||||
<option value="">Select company size</option>
|
||||
<option value="micro">Micro (0-9 employees)</option>
|
||||
<option value="small">Small (10-49 employees)</option>
|
||||
<option value="medium">Medium (50-249 employees)</option>
|
||||
<option value="large">Large (250+ employees)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Company Description</label>
|
||||
<textarea id="description" name="description" placeholder="Briefly describe your company, what you do, and your expertise..."></textarea>
|
||||
<div class="form-help">Helps us match you with more relevant tenders</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Capabilities & Services</h3>
|
||||
<div class="form-group">
|
||||
<label>What services/products do you provide?</label>
|
||||
<div class="tag-input-container" id="capabilitiesInput">
|
||||
<input type="text" class="tag-input" placeholder="Type and press Enter to add...">
|
||||
</div>
|
||||
<div class="form-help">Add tags for your main services or product areas</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Certifications & Accreditations</h3>
|
||||
<div class="form-group">
|
||||
<label>Relevant certifications</label>
|
||||
<div class="multi-select">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="iso9001" name="certifications" value="iso9001">
|
||||
<label for="iso9001">ISO 9001</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="iso27001" name="certifications" value="iso27001">
|
||||
<label for="iso27001">ISO 27001</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="iso14001" name="certifications" value="iso14001">
|
||||
<label for="iso14001">ISO 14001</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="cmmc" name="certifications" value="cmmc">
|
||||
<label for="cmmc">CMMC</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="soc2" name="certifications" value="soc2">
|
||||
<label for="soc2">SOC 2</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="gov.uk" name="certifications" value="gov.uk">
|
||||
<label for="gov.uk">G-Cloud</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn-save" data-section="company">Save Company Profile</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Alert Preferences Section -->
|
||||
<section id="alerts" class="profile-section">
|
||||
<h2>Alert Preferences</h2>
|
||||
<p class="profile-section-desc">Customize how you receive tender alerts and what types of opportunities you want to see.</p>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Tender Keywords</h3>
|
||||
<div class="form-group">
|
||||
<label>Keywords or phrases</label>
|
||||
<div class="tag-input-container" id="keywordsInput">
|
||||
<input type="text" class="tag-input" placeholder="Type and press Enter to add...">
|
||||
</div>
|
||||
<div class="form-help">Enter keywords to match tenders. e.g., 'software development', 'cloud migration'</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Sectors & Categories</h3>
|
||||
<div class="form-group">
|
||||
<label>Which sectors interest you?</label>
|
||||
<div class="multi-select">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-admin" name="sectors" value="admin">
|
||||
<label for="sec-admin">Administration</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-defence" name="sectors" value="defence">
|
||||
<label for="sec-defence">Defence</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-education" name="sectors" value="education">
|
||||
<label for="sec-education">Education</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-energy" name="sectors" value="energy">
|
||||
<label for="sec-energy">Energy</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-environment" name="sectors" value="environment">
|
||||
<label for="sec-environment">Environment</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-health" name="sectors" value="health">
|
||||
<label for="sec-health">Health</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-housing" name="sectors" value="housing">
|
||||
<label for="sec-housing">Housing</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-justice" name="sectors" value="justice">
|
||||
<label for="sec-justice">Justice</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-social" name="sectors" value="social">
|
||||
<label for="sec-social">Social Inclusion</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-transport" name="sectors" value="transport">
|
||||
<label for="sec-transport">Transport</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="sec-utilities" name="sectors" value="utilities">
|
||||
<label for="sec-utilities">Utilities</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Contract Value</h3>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="minValue">Minimum Contract Value (£)</label>
|
||||
<input type="number" id="minValue" name="minValue" placeholder="0" min="0" step="1000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="maxValue">Maximum Contract Value (£)</label>
|
||||
<input type="number" id="maxValue" name="maxValue" placeholder="No limit" min="0" step="1000">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-help">Leave blank for no limit</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Preferred Locations</h3>
|
||||
<div class="form-group">
|
||||
<label>Preferred regions (optional)</label>
|
||||
<div class="multi-select">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="loc-england" name="locations" value="england">
|
||||
<label for="loc-england">England</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="loc-scotland" name="locations" value="scotland">
|
||||
<label for="loc-scotland">Scotland</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="loc-wales" name="locations" value="wales">
|
||||
<label for="loc-wales">Wales</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="loc-ni" name="locations" value="northern-ireland">
|
||||
<label for="loc-ni">Northern Ireland</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Alert Frequency</h3>
|
||||
<div class="form-group">
|
||||
<label for="alertFrequency">How often would you like to receive alerts?</label>
|
||||
<select id="alertFrequency" name="alertFrequency">
|
||||
<option value="instant">Instant (as soon as published)</option>
|
||||
<option value="daily" selected>Daily Digest</option>
|
||||
<option value="weekly">Weekly Digest</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn-save" data-section="alerts">Save Alert Preferences</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Account Section -->
|
||||
<section id="account" class="profile-section">
|
||||
<h2>Account</h2>
|
||||
<p class="profile-section-desc">Manage your account settings and security.</p>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Account Information</h3>
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input type="email" id="email" name="email" aria-required="true" disabled>
|
||||
<div class="form-help">Your primary login email</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Change Password</h3>
|
||||
<div class="form-group">
|
||||
<label for="currentPassword">Current Password</label>
|
||||
<input type="password" id="currentPassword" name="currentPassword" placeholder="Enter your current password">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="newPassword">New Password</label>
|
||||
<input type="password" id="newPassword" name="newPassword" placeholder="Enter your new password">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword">Confirm Password</label>
|
||||
<input type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm your new password">
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn-save" id="changePasswordBtn">Change Password</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h3>Danger Zone</h3>
|
||||
<p style="color: var(--text-secondary); margin-bottom: 1.5rem; font-size: 0.9375rem;">
|
||||
This action cannot be undone. Please be certain.
|
||||
</p>
|
||||
<button class="btn-danger" id="deleteAccountBtn">Delete Account</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Auth and state
|
||||
let authToken = localStorage.getItem('authToken');
|
||||
let currentUser = null;
|
||||
|
||||
// Check authentication
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (!authToken) {
|
||||
window.location.href = '/login.html';
|
||||
return;
|
||||
}
|
||||
|
||||
// Load user profile
|
||||
await loadProfile();
|
||||
|
||||
// Set up event listeners
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
async function loadProfile() {
|
||||
try {
|
||||
const [prefsResponse, userResponse] = await Promise.all([
|
||||
fetch('/api/alerts/preferences', {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
}),
|
||||
fetch('/api/user', {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
}).catch(() => null)
|
||||
]);
|
||||
|
||||
if (!prefsResponse.ok && prefsResponse.status === 401) {
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.href = '/login.html';
|
||||
return;
|
||||
}
|
||||
|
||||
const prefsData = prefsResponse.ok ? await prefsResponse.json() : { preferences: null };
|
||||
const user = userResponse ? await userResponse.json() : null;
|
||||
|
||||
// Set email
|
||||
if (user?.email) {
|
||||
document.getElementById('email').value = user.email;
|
||||
}
|
||||
|
||||
// Load preferences
|
||||
const prefs = prefsData.preferences;
|
||||
if (prefs) {
|
||||
document.getElementById('companyName').value = user?.company_name || '';
|
||||
document.getElementById('minValue').value = prefs.min_value || '';
|
||||
document.getElementById('maxValue').value = prefs.max_value || '';
|
||||
document.getElementById('alertFrequency').value = 'daily'; // Default
|
||||
|
||||
// Load keywords
|
||||
if (prefs.keywords && prefs.keywords.length > 0) {
|
||||
prefs.keywords.forEach(kw => addTag('keywordsInput', kw));
|
||||
}
|
||||
|
||||
// Load sectors
|
||||
if (prefs.sectors && prefs.sectors.length > 0) {
|
||||
prefs.sectors.forEach(sector => {
|
||||
const checkbox = document.querySelector(`input[name="sectors"][value="${sector}"]`);
|
||||
if (checkbox) checkbox.checked = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Load locations
|
||||
if (prefs.locations && prefs.locations.length > 0) {
|
||||
prefs.locations.forEach(location => {
|
||||
const checkbox = document.querySelector(`input[name="locations"][value="${location}"]`);
|
||||
if (checkbox) checkbox.checked = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading profile:', error);
|
||||
showError('Failed to load profile preferences');
|
||||
}
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// Sidebar navigation
|
||||
document.querySelectorAll('.sidebar-link').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const section = link.dataset.section;
|
||||
switchSection(section);
|
||||
});
|
||||
});
|
||||
|
||||
// Save buttons
|
||||
document.querySelectorAll('.btn-save').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const section = btn.dataset.section;
|
||||
await saveSection(section);
|
||||
});
|
||||
});
|
||||
|
||||
// Tag inputs
|
||||
setupTagInput('keywordsInput');
|
||||
setupTagInput('capabilitiesInput');
|
||||
|
||||
// Change password
|
||||
document.getElementById('changePasswordBtn')?.addEventListener('click', async () => {
|
||||
const current = document.getElementById('currentPassword').value;
|
||||
const newPass = document.getElementById('newPassword').value;
|
||||
const confirm = document.getElementById('confirmPassword').value;
|
||||
|
||||
if (!current || !newPass || !confirm) {
|
||||
showError('Please fill all password fields');
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPass !== confirm) {
|
||||
showError('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement password change API endpoint
|
||||
showSuccess('Password change not yet implemented - contact support');
|
||||
});
|
||||
|
||||
// Logout
|
||||
document.getElementById('logoutBtn')?.addEventListener('click', () => {
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.href = '/';
|
||||
});
|
||||
|
||||
// Delete account
|
||||
document.getElementById('deleteAccountBtn')?.addEventListener('click', async () => {
|
||||
if (confirm('Are you absolutely sure? This will permanently delete your account and all associated data.')) {
|
||||
// TODO: Implement account deletion
|
||||
showSuccess('Account deletion not yet implemented - contact support');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function switchSection(section) {
|
||||
// Update sidebar
|
||||
document.querySelectorAll('.sidebar-link').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
});
|
||||
document.querySelector(`[data-section="${section}"]`).classList.add('active');
|
||||
|
||||
// Update main content
|
||||
document.querySelectorAll('.profile-section').forEach(sec => {
|
||||
sec.classList.remove('active');
|
||||
});
|
||||
document.getElementById(section).classList.add('active');
|
||||
}
|
||||
|
||||
async function saveSection(section) {
|
||||
try {
|
||||
const data = {};
|
||||
|
||||
if (section === 'company') {
|
||||
data.keywords = getTags('capabilitiesInput');
|
||||
// TODO: Save company name, industry, size, description
|
||||
} else if (section === 'alerts') {
|
||||
data.keywords = getTags('keywordsInput');
|
||||
data.sectors = getCheckedValues('sectors');
|
||||
data.locations = getCheckedValues('locations');
|
||||
data.min_value = document.getElementById('minValue').value ? parseInt(document.getElementById('minValue').value) : null;
|
||||
data.max_value = document.getElementById('maxValue').value ? parseInt(document.getElementById('maxValue').value) : null;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/alerts/preferences', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
showError(error.error || 'Failed to save preferences');
|
||||
return;
|
||||
}
|
||||
|
||||
showSuccess(`${section === 'company' ? 'Company Profile' : 'Alert Preferences'} saved successfully!`);
|
||||
} catch (error) {
|
||||
console.error('Error saving:', error);
|
||||
showError('Failed to save preferences');
|
||||
}
|
||||
}
|
||||
|
||||
function setupTagInput(containerId) {
|
||||
const container = document.getElementById(containerId);
|
||||
const input = container.querySelector('.tag-input');
|
||||
|
||||
container.addEventListener('click', () => {
|
||||
input.focus();
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const value = input.value.trim();
|
||||
if (value) {
|
||||
addTag(containerId, value);
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('focus', () => {
|
||||
container.classList.add('focused');
|
||||
});
|
||||
|
||||
input.addEventListener('blur', () => {
|
||||
container.classList.remove('focused');
|
||||
});
|
||||
}
|
||||
|
||||
function addTag(containerId, value) {
|
||||
const container = document.getElementById(containerId);
|
||||
const input = container.querySelector('.tag-input');
|
||||
|
||||
const tag = document.createElement('div');
|
||||
tag.className = 'tag';
|
||||
tag.innerHTML = `
|
||||
${value}
|
||||
<button type="button">×</button>
|
||||
`;
|
||||
|
||||
tag.querySelector('button').addEventListener('click', () => {
|
||||
tag.remove();
|
||||
});
|
||||
|
||||
container.insertBefore(tag, input);
|
||||
}
|
||||
|
||||
function getTags(containerId) {
|
||||
const container = document.getElementById(containerId);
|
||||
return Array.from(container.querySelectorAll('.tag'))
|
||||
.map(tag => tag.textContent.trim().replace('×', '').trim());
|
||||
}
|
||||
|
||||
function getCheckedValues(name) {
|
||||
return Array.from(document.querySelectorAll(`input[name="${name}"]:checked`))
|
||||
.map(cb => cb.value);
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
const el = document.getElementById('successMessage');
|
||||
el.textContent = message;
|
||||
el.classList.add('show');
|
||||
setTimeout(() => el.classList.remove('show'), 5000);
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
const el = document.getElementById('errorMessage');
|
||||
el.textContent = message;
|
||||
el.classList.add('show');
|
||||
setTimeout(() => el.classList.remove('show'), 5000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user