fix(seo): comprehensive SEO improvements per expert audit #4
No files matched your search
@@ -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]
|
||||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||||
|
|||||||
@@ -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="")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Disallow: /admin
|
||||||
|
Disallow: /login
|
||||||
|
|
||||||
|
Sitemap: https://kmountainflower.org/sitemap.xml
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,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 →</a>
|
<a [href]="event.rsvp_url" class="event-rsvp" target="_blank" rel="noopener">RSVP →</a>
|
||||||
} @else {
|
} @else {
|
||||||
<a href="#" class="event-rsvp">Learn More →</a>
|
<a routerLink="/contact" [queryParams]="{subject: 'general'}" class="event-rsvp">Learn More →</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
|||||||
|
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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user
I don't think we've added any admin UI to set these values.