<a href="#main-content" class="skip-link">Skip to main content</a>
<div class="container">
<header class="header">
<h1>Community Stories</h1>
<p>Share impactful client stories through engaging newsletters</p>
</header>
<nav role="navigation" aria-label="Main navigation">
<div class="tabs">
<button class="tab active" onclick="showTab('submit')" aria-controls="submit" aria-selected="true">
Submit Story
</button>
<button class="tab" onclick="showTab('browse')" aria-controls="browse" aria-selected="false">
Browse Stories
</button>
<button class="tab" onclick="showTab('newsletter')" aria-controls="newsletter" aria-selected="false">
Create Newsletter
</button>
</div>
</nav>
<main id="main-content" role="main">
<div id="submit" class="tab-content active" role="tabpanel" aria-labelledby="submit-tab">
<div class="card">
<h2 style="margin-bottom: 20px; color: #2c3e50;">Submit a Client Story</h2>
<form id="storyForm" novalidate>
<div class="form-group">
<label for="orgName" class="required">Organization Name</label>
<input type="text" id="orgName" name="orgName" required aria-describedby="orgName-error">
<div id="orgName-error" class="error-message" role="alert" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="storyTitle" class="required">Story Title</label>
<input type="text" id="storyTitle" name="storyTitle" required aria-describedby="storyTitle-error">
<div id="storyTitle-error" class="error-message" role="alert" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="storyType" class="required">Story Type</label>
<select id="storyType" name="storyType" required aria-describedby="storyType-error">
<option value="">Select type...</option>
<option value="audio">Audio Recording</option>
<option value="video">Video Recording</option>
<option value="written">Written Story</option>
</select>
<div id="storyType-error" class="error-message" role="alert" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="storyDescription">Story Description</label>
<textarea id="storyDescription" name="storyDescription" rows="4"
placeholder="Brief description of the story's impact and message..."
aria-describedby="storyDescription-help"></textarea>
<div id="storyDescription-help" style="font-size: 0.9rem; color: #666; margin-top: 5px;">
Provide a brief summary of the story's key message and impact
</div>
</div>
<div class="form-group">
<label for="clientConsent" class="required">Client Consent</label>
<select id="clientConsent" name="clientConsent" required aria-describedby="clientConsent-error">
<option value="">Select consent status...</option>
<option value="full">Full consent for sharing</option>
<option value="anonymous">Anonymous sharing only</option>
<option value="internal">Internal use only</option>
</select>
<div id="clientConsent-error" class="error-message" role="alert" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="storyFile">Upload Story File</label>
<div class="file-upload">
<input type="file" id="storyFile" name="storyFile"
accept="audio/*,video/*,.pdf,.doc,.docx"
aria-describedby="file-help">
<div class="file-upload-text">
<p>📁 Click to upload audio, video, or document</p>
<p id="file-help" style="font-size: 0.9rem; color: #666; margin-top: 10px;">
Supported formats: MP3, MP4, PDF, DOC, DOCX
</p>
</div>
</div>
</div>
<div class="form-group">
<label for="tags">Tags (comma-separated)</label>
<input type="text" id="tags" name="tags"
placeholder="e.g., housing, mental health, youth services"
aria-describedby="tags-help">
<div id="tags-help" style="font-size: 0.9rem; color: #666; margin-top: 5px;">
Add keywords to help categorize your story
</div>
</div>
<button type="submit" class="btn">Submit Story</button>
<div id="submitSuccess" class="success-message" role="alert" aria-live="polite">
Story submitted successfully! It will be reviewed and added to the library.
</div>
</form>
</div>
</div>
<div id="browse" class="tab-content" role="tabpanel" aria-labelledby="browse-tab">
<div class="card">
<h2 style="margin-bottom: 20px; color: #2c3e50;">Browse Client Stories</h2>
<div class="filter-controls" role="group" aria-label="Filter stories">
<select id="filterOrg" aria-label="Filter by organization" onchange="filterStories()">
<option value="">All Organizations</option>
<option value="Hope Center">Hope Center</option>
<option value="Community Kitchen">Community Kitchen</option>
<option value="Youth Services">Youth Services</option>
</select>
<select id="filterType" aria-label="Filter by story type" onchange="filterStories()">
<option value="">All Types</option>
<option value="audio">Audio</option>
<option value="video">Video</option>
<option value="written">Written</option>
</select>
<select id="filterTag" aria-label="Filter by tag" onchange="filterStories()">
<option value="">All Tags</option>
<option value="housing">Housing</option>
<option value="mental health">Mental Health</option>
<option value="youth services">Youth Services</option>
</select>
</div>
<div class="story-grid" id="storyGrid" role="region" aria-label="Story collection">
<!-- Stories will be populated here -->
</div>
</div>
</div>
<div id="newsletter" class="tab-content" role="tabpanel" aria-labelledby="newsletter-tab">
<div class="card">
<h2 style="margin-bottom: 20px; color: #2c3e50;">Create Newsletter</h2>
<div class="form-group">
<label for="newsletterTitle">Newsletter Title</label>
<input type="text" id="newsletterTitle" placeholder="Community Impact - March 2024">
</div>
<div class="form-group">
<label for="newsletterIntro">Introduction</label>
<textarea id="newsletterIntro" rows="3" placeholder="Welcome message for your newsletter..."></textarea>
</div>
<div class="form-group">
<label>Select Stories to Include</label>
<div id="storySelection" class="checkbox-group" role="group" aria-label="Story selection">
<!-- Story checkboxes will be populated here -->
</div>
</div>
<button class="btn" onclick="generateNewsletter()">Generate Newsletter Preview</button>
</div>
<div id="newsletterPreview" class="newsletter-preview" style="display: none;" role="region" aria-label="Newsletter preview">
<div class="newsletter-header">
<h1 class="newsletter-title" id="previewTitle">Community Impact Newsletter</h1>
<p class="newsletter-date" id="previewDate">March 2024</p>
</div>
<div id="previewContent">
<!-- Newsletter content will be generated here -->
</div>
<div style="text-align: center; margin-top: 30px;">
<button class="btn" onclick="exportNewsletter()">Export as HTML</button>
<button class="btn" onclick="sendNewsletter()" style="margin-left: 10px;">Send Newsletter</button>
</div>
</div>
</div>
</main>
</div>
<script>
// Sample data
let stories = [
{
id: 1,
title: "Finding Hope After Homelessness",
organization: "Hope Center",
type: "audio",
description: "Maria shares her journey from living on the streets to finding stable housing and employment through our support programs.",
tags: ["housing", "employment"],
consent: "full",
file: "audio_story_1.mp3",
selected: false
},
{
id: 2,
title: "Youth Mentorship Success",
organization: "Youth Services",
type: "video",
description: "16-year-old James talks about how mentorship helped him graduate high school and plan for college.",
tags: ["youth services", "education"],
consent: "full",
file: "video_story_1.mp4",
selected: false
},
{
id: 3,
title: "Mental Health Recovery",
organization: "Community Kitchen",
type: "written",
description: "Anonymous client shares their path to mental health recovery through our peer support groups.",
tags: ["mental health", "support groups"],
consent: "anonymous",
file: "written_story_1.pdf",
selected: false
}
];
// Tab switching with accessibility
function showTab(tabName) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Remove active class and aria-selected from all tabs
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
tab.setAttribute('aria-selected', 'false');
});
// Show selected tab content
document.getElementById(tabName).classList.add('active');
// Add active class and aria-selected to clicked tab
event.target.classList.add('active');
event.target.setAttribute('aria-selected', 'true');
// Announce tab change to screen readers
const tabContent = document.getElementById(tabName);
const heading = tabContent.querySelector('h2');
if (heading) {
heading.focus();
}
// Load content based on tab
if (tabName === 'browse') {
loadStories();
} else if (tabName === 'newsletter') {
loadStorySelection();
}
}
// Form validation
function validateForm() {
const form = document.getElementById('storyForm');
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
const errorElement = document.getElementById(field.id + '-error');
if (!field.value.trim()) {
errorElement.textContent = `${field.labels[0].textContent.replace(' *', '')} is required`;
errorElement.style.display = 'block';
field.setAttribute('aria-invalid', 'true');
isValid = false;
} else {
errorElement.style.display = 'none';
field.setAttribute('aria-invalid', 'false');
}
});
return isValid;
}
// Form submission with validation
document.getElementById('storyForm').addEventListener('submit', function(e) {
e.preventDefault();
if (!validateForm()) {
return;
}
const formData = new FormData(this);
const storyData = {
id: stories.length + 1,
title: formData.get('storyTitle'),
organization: formData.get('orgName'),
type: formData.get('storyType'),
description: formData.get('storyDescription'),
tags: formData.get('tags').split(',').map(tag => tag.trim()).filter(tag => tag),
consent: formData.get('clientConsent'),
file: formData.get('storyFile').name,
selected: false
};
stories.push(storyData);
// Show success message
const successMessage = document.getElementById('submitSuccess');
successMessage.style.display = 'block';
successMessage.focus();
// Reset form
this.reset();
// Clear any validation errors
document.querySelectorAll('.error-message').forEach(error => {
error.style.display = 'none';
});
// Hide success message after 5 seconds
setTimeout(() => {
successMessage.style.display = 'none';
}, 5000);
});
// Real-time validation
document.querySelectorAll('#storyForm [required]').forEach(field => {
field.addEventListener('blur', function() {
const errorElement = document.getElementById(this.id + '-error');
if (!this.value.trim()) {
errorElement.textContent = `${this.labels[0].textContent.replace(' *', '')} is required`;
errorElement.style.display = 'block';
this.setAttribute('aria-invalid', 'true');
} else {
errorElement