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]
|
||||||
|
kfj001 marked this conversation as resolved
Outdated
|
|||||||
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"?>
|
||||||
|
kfj001
commented
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>
|
||||||
@@ -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
kfj001
commented
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;
|
||||||
|
|||||||
@@ -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
There has got to be a more general way to ensure this runs on any branch on push.