fix(seo): comprehensive SEO improvements per expert audit #4

Merged
robot merged 8 commits from fix/seo-improvements into main 2026-08-04 20:03:55 -07:00
28 changed files with 83 additions and 14 deletions
Showing only changes of commit bea1c97e88 - Show all commits

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ name: CI Pipeline
on: on:
push: push:
branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles] branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles, fix/seo-improvements]
kfj001 marked this conversation as resolved Outdated
Outdated
Review

There has got to be a more general way to ensure this runs on any branch on push.

There has got to be a more general way to ensure this runs on any branch on push.
pull_request: pull_request:
branches: [main] branches: [main]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5
View File
@@ -78,6 +78,11 @@ def _migrate_add_missing_columns(sync_conn):
("station_config", "color_black", sa.String(7), "#1a1816"), ("station_config", "color_black", sa.String(7), "#1a1816"),
# Multi-admin support: password_hash (nullable — existing rows stay NULL) # Multi-admin support: password_hash (nullable — existing rows stay NULL)
("users", "password_hash", sa.String(255), None), ("users", "password_hash", sa.String(255), None),
# Social media columns
("station_config", "social_facebook", sa.String(500), ""),
("station_config", "social_twitter", sa.String(500), ""),
("station_config", "social_instagram", sa.String(500), ""),
("station_config", "social_youtube", sa.String(500), ""),
] ]
# Determine dialect # Determine dialect
+6
View File
@@ -97,6 +97,12 @@ class StationConfig(Base):
# Donation # Donation
donation_url = Column(String(500), nullable=False, default="") donation_url = Column(String(500), nullable=False, default="")
# Social media
social_facebook = Column(String(500), nullable=False, default="")
social_twitter = Column(String(500), nullable=False, default="")
social_instagram = Column(String(500), nullable=False, default="")
social_youtube = Column(String(500), nullable=False, default="")
# Stream # Stream
stream_url = Column(String(500), nullable=False, default="") stream_url = Column(String(500), nullable=False, default="")
stream_metadata_url = Column(String(500), nullable=False, default="") stream_metadata_url = Column(String(500), nullable=False, default="")
+10
View File
@@ -78,6 +78,11 @@ class StationConfigResponse(BaseModel):
play_store_icon_url: str play_store_icon_url: str
play_store_url: str play_store_url: str
app_store_embed_html: str app_store_embed_html: str
# Social media
social_facebook: str
social_twitter: str
social_instagram: str
social_youtube: str
# Page content # Page content
hero_subtitle: str hero_subtitle: str
about_intro: str about_intro: str
@@ -129,6 +134,11 @@ class StationConfigUpdate(BaseModel):
play_store_icon_url: Optional[str] = None play_store_icon_url: Optional[str] = None
play_store_url: Optional[str] = None play_store_url: Optional[str] = None
app_store_embed_html: Optional[str] = None app_store_embed_html: Optional[str] = None
# Social media
social_facebook: Optional[str] = None
social_twitter: Optional[str] = None
social_instagram: Optional[str] = None
social_youtube: Optional[str] = None
# Page content # Page content
hero_subtitle: Optional[str] = None hero_subtitle: Optional[str] = None
about_intro: Optional[str] = None about_intro: Optional[str] = None
+7
View File
@@ -0,0 +1,7 @@
User-agent: *
Allow: /
Disallow: /admin
Disallow: /login
Sitemap: https://kmountainflower.org/sitemap.xml
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
Outdated
Review

This is a whitelabel app - this file may need to be dynaimcally generated...

This is a whitelabel app - this file may need to be dynaimcally generated...
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://kmountainflower.org/</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://kmountainflower.org/about</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://kmountainflower.org/schedule</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://kmountainflower.org/events</loc>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://kmountainflower.org/contact</loc>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
+2 -2
View File
@@ -3,7 +3,7 @@
<!-- Intro --> <!-- Intro -->
<div class="about-intro"> <div class="about-intro">
<img [src]="resolveImageUrl(config().hero_icon_url)" alt="" class="about-flower-decor" aria-hidden="true"> <img [src]="resolveImageUrl(config().hero_icon_url)" alt="" class="about-flower-decor" aria-hidden="true">
<h2>About <span class="text-accent">{{ config().name_primary }} {{ config().name_secondary }}</span></h2> <h1>About <span class="text-accent">{{ config().name_primary }} {{ config().name_secondary }}</span></h1>
<p class="section-intro">{{ config().about_intro }}</p> <p class="section-intro">{{ config().about_intro }}</p>
</div> </div>
@@ -73,7 +73,7 @@
@if (config().donation_url) { @if (config().donation_url) {
<div class="about-cta"> <div class="about-cta">
<div class="about-flower-divider" aria-hidden="true"> <div class="about-flower-divider" aria-hidden="true">
<img [src]="resolveImageUrl(config().logo_url)" alt=""> <img [src]="resolveImageUrl(config().logo_url)" alt="" aria-hidden="true">
</div> </div>
<h3>Become a Member</h3> <h3>Become a Member</h3>
<p class="section-intro">{{ config().about_donation_text }}</p> <p class="section-intro">{{ config().about_donation_text }}</p>
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="container"> <div class="container">
<div class="contact-header"> <div class="contact-header">
<img src="svg/small-flower.svg" alt="" class="contact-flower" aria-hidden="true"> <img src="svg/small-flower.svg" alt="" class="contact-flower" aria-hidden="true">
<h2>Get in <span class="text-accent">Touch</span></h2> <h1>Get in <span class="text-accent">Touch</span></h1>
<p class="section-intro"> <p class="section-intro">
Have a song request, want to volunteer, or just want to say hello? Have a song request, want to volunteer, or just want to say hello?
We'd love to hear from you. We'd love to hear from you.
+2 -2
View File
@@ -2,7 +2,7 @@
<div class="container"> <div class="container">
<div class="events-header"> <div class="events-header">
<img src="svg/small-flower.svg" alt="" class="events-flower" aria-hidden="true"> <img src="svg/small-flower.svg" alt="" class="events-flower" aria-hidden="true">
<h2>Upcoming <span class="text-accent">Events</span></h2> <h1>Upcoming <span class="text-accent">Events</span></h1>
<p class="section-intro">{{ config().events_intro }}</p> <p class="section-intro">{{ config().events_intro }}</p>
</div> </div>
@@ -29,7 +29,7 @@
@if (event.rsvp_url) { @if (event.rsvp_url) {
<a [href]="event.rsvp_url" class="event-rsvp" target="_blank" rel="noopener">RSVP &rarr;</a> <a [href]="event.rsvp_url" class="event-rsvp" target="_blank" rel="noopener">RSVP &rarr;</a>
} @else { } @else {
<a href="#" class="event-rsvp">Learn More &rarr;</a> <a routerLink="/contact" [queryParams]="{subject: 'general'}" class="event-rsvp">Learn More &rarr;</a>
} }
</div> </div>
</div> </div>
+15 -7
View File
@@ -35,18 +35,26 @@
@if (config().donation_url) { @if (config().donation_url) {
<li><a [href]="config().donation_url" target="_blank" rel="noopener noreferrer">Become a Member</a></li> <li><a [href]="config().donation_url" target="_blank" rel="noopener noreferrer">Become a Member</a></li>
} }
<li><a href="#">Volunteer</a></li> <li><a routerLink="/contact" [queryParams]="{subject: 'volunteer'}">Volunteer</a></li>
<li><a href="#">Corporate Giving</a></li> <li><a routerLink="/contact" [queryParams]="{subject: 'advertising'}">Corporate Giving</a></li>
<li><a href="#">Planned Giving</a></li> <li><a routerLink="/contact" [queryParams]="{subject: 'general'}">Planned Giving</a></li>
</ul> </ul>
</div> </div>
<div class="footer-col"> <div class="footer-col">
<h4>Follow Us</h4> <h4>Follow Us</h4>
<div class="social-links"> <div class="social-links">
<a href="#" aria-label="Facebook" class="social-link">📘</a> @if (config().social_facebook) {
<a href="#" aria-label="Twitter" class="social-link">🐦</a> <a [href]="config().social_facebook" target="_blank" rel="noopener noreferrer" aria-label="Facebook" class="social-link">📘</a>
<a href="#" aria-label="Instagram" class="social-link">📷</a> }
<a href="#" aria-label="YouTube" class="social-link">📺</a> @if (config().social_twitter) {
<a [href]="config().social_twitter" target="_blank" rel="noopener noreferrer" aria-label="Twitter" class="social-link">🐦</a>
}
@if (config().social_instagram) {
<a [href]="config().social_instagram" target="_blank" rel="noopener noreferrer" aria-label="Instagram" class="social-link">📷</a>
}
@if (config().social_youtube) {
<a [href]="config().social_youtube" target="_blank" rel="noopener noreferrer" aria-label="YouTube" class="social-link">📺</a>
}
</div> </div>
</div> </div>
</div> </div>
+5
View File
@@ -23,6 +23,11 @@ export interface StationConfig {
play_store_icon_url: string; play_store_icon_url: string;
play_store_url: string; play_store_url: string;
app_store_embed_html: string; app_store_embed_html: string;
// Social media
kfj001 marked this conversation as resolved
Review

I don't think we've added any admin UI to set these values.

I don't think we've added any admin UI to set these values.
social_facebook: string;
social_twitter: string;
social_instagram: string;
social_youtube: string;
// Page content // Page content
hero_subtitle: string; hero_subtitle: string;
about_intro: string; about_intro: string;
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="container"> <div class="container">
<div class="schedule-header"> <div class="schedule-header">
<img [src]="resolveImageUrl(config().logo_url)" alt="" class="schedule-flower" aria-hidden="true"> <img [src]="resolveImageUrl(config().logo_url)" alt="" class="schedule-flower" aria-hidden="true">
<h2>Program <span class="text-accent">Schedule</span></h2> <h1>Program <span class="text-accent">Schedule</span></h1>
<p class="section-intro">{{ config().schedule_intro }}</p> <p class="section-intro">{{ config().schedule_intro }}</p>
</div> </div>