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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ========================================
# FIREBASE ENVIRONMENT VARIABLES TEMPLATE
# ========================================
#
# ARCHITECTURE:
# - These variables are stored in .env (server-side)
# - Server endpoint /api/config serves them to client
# - Client loads via scripts/env.js before Firebase initialization
# - Never expose .env to public repository
#
# SETUP INSTRUCTIONS:
# 1. Copy this file: cp .env.example .env
# 2. Get your Firebase project config from Firebase Console
# 3. Fill in your actual values below
# 4. NEVER push .env file to GitHub (it's in .gitignore)
# 5. Each developer/environment needs their own .env file
# 6. Server must be running for client to fetch config via /api/config
#
# ========================================

# API Key - Allows app to connect to your Firebase project
# Get from: Firebase Console > Project Settings > Web App Config
VITE_FIREBASE_API_KEY=your_firebase_api_key_here

# Auth Domain - Used for user login/signup
# Format: your-project-name.firebaseapp.com
VITE_FIREBASE_AUTH_DOMAIN=your_project_id.firebaseapp.com

# Project ID - Unique identifier for your Firebase project
# Get from: Firebase Console > Project Settings
VITE_FIREBASE_PROJECT_ID=your_project_id

# Storage Bucket - Where user files are stored
# Format: your-project-name.firebasestorage.app
VITE_FIREBASE_STORAGE_BUCKET=your_project_id.firebasestorage.app

# Messaging Sender ID - For push notifications (if used)
# Get from: Firebase Console > Project Settings
VITE_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id

# App ID - Identifies this web app in Firebase
# Format: 1:number:web:random-id
VITE_FIREBASE_APP_ID=1:your_app_id:web:your_web_app_id

# Measurement ID - For Google Analytics (optional)
# Get from: Firebase Console > Google Analytics settings
VITE_FIREBASE_MEASUREMENT_ID=G-your_measurement_id
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ yarn-error.log
.env
.env.local
.env.*.local
env.js

# IDE
.vscode/
Expand Down
199 changes: 178 additions & 21 deletions contact.html
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,38 @@
.contact-form button:hover {
background: #764ba2;
}
.contact-form .form-group {
display: flex;
flex-direction: column;
gap: 0.3rem;
position: relative;
}
.contact-form .form-help,
.contact-form .error-message {
font-size: 0.85rem;
margin-top: 0.2rem;
}
.contact-form .form-help {
color: #aab4e8;
}
.contact-form .error-message {
color: #ff6b6b;
display: none;
}
.contact-form .form-group.has-error .error-message {
display: block;
}
.contact-form .form-group.has-error input,
.contact-form .form-group.has-error textarea {
border-color: #ff6b6b;
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.1);
}
.contact-form .message-counter {
font-size: 0.85rem;
color: #aab4e8;
text-align: right;
margin-top: 0.2rem;
}
.contact-details {
margin-top: 2.5rem;
color: #e4e8f7;
Expand Down Expand Up @@ -500,11 +532,24 @@ <h1>Contact Us</h1>
<p>
We'd love to hear from you! Fill out the form below and our team will get back to you soon.
</p>
<form class="contact-form" autocomplete="off">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<textarea name="message" rows="5" placeholder="Your Message" required></textarea>
<button type="submit">Send Message</button>
<form id="contact-form" class="contact-form" autocomplete="off" novalidate>
<div class="form-group">
<input type="text" id="contact-name" name="name" placeholder="Your Name" minlength="2" maxlength="50" required aria-describedby="nameHelp">
<small id="nameHelp" class="form-help">Full name (2-50 characters)</small>
<span class="error-message" id="nameError"></span>
</div>
<div class="form-group">
<input type="email" id="contact-email" name="email" placeholder="Your Email" required aria-describedby="emailHelp">
<small id="emailHelp" class="form-help">Valid email address</small>
<span class="error-message" id="emailError"></span>
</div>
<div class="form-group">
<textarea id="contact-message" name="message" rows="5" placeholder="Your Message" minlength="10" maxlength="1000" required aria-describedby="messageHelp"></textarea>
<small id="messageHelp" class="form-help">Your message (10-1000 characters)</small>
<span class="error-message" id="messageError"></span>
<div class="message-counter"><span id="messageCount">0</span>/1000</div>
</div>
<button type="submit" id="contactSubmitBtn">Send Message</button>
</form>
<div class="contact-details">
<p><strong>Email:</strong> <a href="mailto:support@venturalink.com">support@venturalink.com</a></p>
Expand Down Expand Up @@ -549,34 +594,146 @@ <h2 id="modalTitle"></h2>
<script src="scripts/cursor.js"></script>

<script>
const form = document.querySelector('.contact-form');
const form = document.getElementById('contact-form');
const nameInput = document.getElementById('contact-name');
const emailInput = document.getElementById('contact-email');
const messageInput = document.getElementById('contact-message');
const messageCount = document.getElementById('messageCount');
const modal = document.getElementById('notificationModal');
const modalTitle = document.getElementById('modalTitle');
const modalMessage = document.getElementById('modalMessage');
const closeModal = document.getElementById('closeModal');
const submitBtn = document.getElementById('contactSubmitBtn');

// Validation functions
function validateName(value) {
return value && value.trim().length >= 2 && value.trim().length <= 50;
}

function validateEmail(value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return value && emailRegex.test(value);
}

// Simulated backend (replace later with actual API)
async function submitForm(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.2 ? resolve({ ok: true }) : reject();
}, 1000);
});
function validateMessage(value) {
return value && value.trim().length >= 10 && value.trim().length <= 1000;
}

function showFieldError(fieldId, errorId, message) {
const field = document.getElementById(fieldId);
const errorElement = document.getElementById(errorId);
const formGroup = field.closest('.form-group');

formGroup.classList.add('has-error');
errorElement.textContent = message;
}

function clearFieldError(fieldId, errorId) {
const field = document.getElementById(fieldId);
const errorElement = document.getElementById(errorId);
const formGroup = field.closest('.form-group');

formGroup.classList.remove('has-error');
errorElement.textContent = '';
}

// Character counter for message
messageInput.addEventListener('input', () => {
messageCount.textContent = messageInput.value.length;

if (messageInput.value.length > messageInput.maxLength * 0.8) {
messageCount.style.color = '#ffa500';
} else if (messageInput.value.length > messageInput.maxLength * 0.95) {
messageCount.style.color = '#ff6b6b';
} else {
messageCount.style.color = '#aab4e8';
}
});

// Real-time validation
nameInput.addEventListener('blur', () => {
if (!nameInput.value) {
showFieldError('contact-name', 'nameError', 'Name is required');
} else if (!validateName(nameInput.value)) {
showFieldError('contact-name', 'nameError', 'Name must be between 2-50 characters');
} else {
clearFieldError('contact-name', 'nameError');
}
});

emailInput.addEventListener('blur', () => {
if (!emailInput.value) {
showFieldError('contact-email', 'emailError', 'Email is required');
} else if (!validateEmail(emailInput.value)) {
showFieldError('contact-email', 'emailError', 'Please enter a valid email address');
} else {
clearFieldError('contact-email', 'emailError');
}
});

messageInput.addEventListener('blur', () => {
if (!messageInput.value) {
showFieldError('contact-message', 'messageError', 'Message is required');
} else if (!validateMessage(messageInput.value)) {
showFieldError('contact-message', 'messageError', 'Message must be between 10-1000 characters');
} else {
clearFieldError('contact-message', 'messageError');
}
});

// Form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());

// Validate all fields
let isFormValid = true;

if (!validateName(nameInput.value)) {
showFieldError('contact-name', 'nameError', 'Name must be between 2-50 characters');
isFormValid = false;
} else {
clearFieldError('contact-name', 'nameError');
}

if (!validateEmail(emailInput.value)) {
showFieldError('contact-email', 'emailError', 'Please enter a valid email address');
isFormValid = false;
} else {
clearFieldError('contact-email', 'emailError');
}

if (!validateMessage(messageInput.value)) {
showFieldError('contact-message', 'messageError', 'Message must be between 10-1000 characters');
isFormValid = false;
} else {
clearFieldError('contact-message', 'messageError');
}

if (!isFormValid) return;

// Submit form
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';

try {
const response = await submitForm(data);
if (response.ok) {
form.reset();
showModal("Success!", "Your message has been sent successfully!");
}
const formData = new FormData(form);
const data = Object.fromEntries(formData.entries());

// Simulated backend call (replace with actual API)
await new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.2 ? resolve({ ok: true }) : reject();
}, 1000);
});

form.reset();
messageCount.textContent = '0';
showModal('Success!', 'Your message has been sent successfully! We will get back to you soon.');
} catch (error) {
showModal("Error", "Something went wrong, please try again.");
showModal('Error', 'Something went wrong, please try again.');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Send Message';
}
});

Expand Down
25 changes: 14 additions & 11 deletions create-proposal.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ <h2>Basic Information</h2>

<div class="form-grid">
<div class="form-group floating-label">
<input type="text" id="title" name="title" required placeholder=" ">
<input type="text" id="title" name="title" required placeholder="e.g., TechFlow Solutions" aria-describedby="titleHelp" tabindex="1">
<label for="title">Business Name / Title</label>
<small id="titleHelp" class="field-help">Enter your business name or startup title</small>
<div class="input-border"></div>
<div class="field-icon">
<svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24">
Expand All @@ -145,7 +146,7 @@ <h2>Basic Information</h2>
</div>

<div class="form-group custom-select">
<select id="category" name="category" required>
<select id="category" name="category" required aria-describedby="categoryHelp" tabindex="2">
<option value="">Select a category</option>
<option value="technology">🚀 Technology</option>
<option value="healthcare">🏥 Healthcare</option>
Expand All @@ -156,6 +157,7 @@ <h2>Basic Information</h2>
<option value="other">🔧 Other</option>
</select>
<label for="category">Business Category</label>
<small id="categoryHelp" class="field-help">Select the industry your business operates in</small>
<div class="select-arrow">
<svg width="18" height="18" fill="currentColor" viewBox="0 0 24 24">
<path d="M7 10l5 5 5-5z"/>
Expand All @@ -181,21 +183,21 @@ <h2>Business Details</h2>

<div class="form-grid">
<div class="form-group floating-label full-width">
<textarea id="description" name="description" required rows="4" placeholder=" "></textarea>
<textarea id="description" name="description" required rows="4" placeholder="Describe your business, vision, and what makes it unique..." maxlength="500"></textarea>
<label for="description">Business Description</label>
<div class="input-border"></div>
<div class="char-counter" data-target="description" data-max="500">0/500</div>
</div>

<div class="form-group floating-label">
<textarea id="problem" name="problem" required rows="3" placeholder=" "></textarea>
<textarea id="problem" name="problem" required rows="3" placeholder="What specific problem does your business solve?" maxlength="300"></textarea>
<label for="problem">Problem Statement</label>
<div class="input-border"></div>
<div class="char-counter" data-target="problem" data-max="300">0/300</div>
</div>

<div class="form-group floating-label">
<textarea id="solution" name="solution" required rows="3" placeholder=" "></textarea>
<textarea id="solution" name="solution" required rows="3" placeholder="How does your solution address the problem?" maxlength="300"></textarea>
<label for="solution">Your Solution</label>
<div class="input-border"></div>
<div class="char-counter" data-target="solution" data-max="300">0/300</div>
Expand All @@ -219,21 +221,21 @@ <h2>Investment Details</h2>

<div class="form-grid">
<div class="form-group floating-label currency-input">
<input type="number" id="amount" name="amount" required min="1000" placeholder=" ">
<input type="text" id="amount" name="amount" required placeholder="e.g., 500000" data-currency="true">
<label for="amount">Investment Amount Needed</label>
<div class="currency-symbol">$</div>
<div class="input-border"></div>
</div>

<div class="form-group floating-label percentage-input">
<input type="number" id="equity" name="equity" min="0" max="100" placeholder=" ">
<input type="number" id="equity" name="equity" min="0" max="100" placeholder="e.g., 10">
<label for="equity">Equity Offered</label>
<div class="percentage-symbol">%</div>
<div class="input-border"></div>
</div>

<div class="form-group floating-label full-width">
<textarea id="useOfFunds" name="useOfFunds" required rows="3" placeholder=" "></textarea>
<textarea id="useOfFunds" name="useOfFunds" required rows="3" placeholder="How will you use the investment funds? (R&D, Marketing, Operations, etc.)" maxlength="400"></textarea>
<label for="useOfFunds">Use of Funds</label>
<div class="input-border"></div>
<div class="char-counter" data-target="useOfFunds" data-max="400">0/400</div>
Expand All @@ -257,21 +259,21 @@ <h2>Additional Information</h2>

<div class="form-grid">
<div class="form-group floating-label">
<textarea id="marketSize" name="marketSize" rows="2" placeholder=" "></textarea>
<textarea id="marketSize" name="marketSize" rows="2" placeholder="Estimate your addressable market size and growth potential..." maxlength="250"></textarea>
<label for="marketSize">Market Size & Potential</label>
<div class="input-border"></div>
<div class="char-counter" data-target="marketSize" data-max="250">0/250</div>
</div>

<div class="form-group floating-label">
<textarea id="competitors" name="competitors" rows="2" placeholder=" "></textarea>
<textarea id="competitors" name="competitors" rows="2" placeholder="Who are your competitors and what makes your solution different?" maxlength="250"></textarea>
<label for="competitors">Competitive Landscape</label>
<div class="input-border"></div>
<div class="char-counter" data-target="competitors" data-max="250">0/250</div>
</div>

<div class="form-group floating-label full-width">
<textarea id="timeline" name="timeline" rows="2" placeholder=" "></textarea>
<textarea id="timeline" name="timeline" rows="2" placeholder="Outline key milestones and timeline for achieving your business goals..." maxlength="300"></textarea>
<label for="timeline">Business Timeline & Milestones</label>
<div class="input-border"></div>
<div class="char-counter" data-target="timeline" data-max="300">0/300</div>
Expand Down Expand Up @@ -338,6 +340,7 @@ <h2>Additional Information</h2>
})();
</script>

<script src="./scripts/env.js"></script>
<script type="module" src="./scripts/create-proposal.js"></script>
</body>
</html>
Loading