max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
7 files changed,
+17 insertions,
-115 deletions
| @@ -2,12 +2,19 @@ | |||
| 2 | 2 | //! | |
| 3 | 3 | //! Shape 8's first conversion that did not have to wait for a screen | |
| 4 | 4 | //! (`b279b9eb`), and the second thing the glue-module ruling bought | |
| 5 | - | //! (`27d5e5b8`, reasoning in [`super::widgets`]). The same button is written | |
| 6 | - | //! twice — `templates/partials/tabs/project_content.html` and | |
| 7 | - | //! `templates/partials/tabs/project_blog.html` — over the same route, with the | |
| 5 | + | //! (`27d5e5b8`, reasoning in [`super::widgets`]). The same button was written | |
| 6 | + | //! twice, in `templates/partials/tabs/project_content.html` and | |
| 7 | + | //! `templates/partials/tabs/project_blog.html`, over the same route with the | |
| 8 | 8 | //! same prompt, in two tabs of the same dashboard. Described once, called from | |
| 9 | 9 | //! both. | |
| 10 | 10 | //! | |
| 11 | + | //! **One caller now.** The blog tab was dead — a route no markup reached, kept | |
| 12 | + | //! only because it held the second copy of this table — and it was deleted at | |
| 13 | + | //! the `project_content` flip (`6077d0d9`, 2026-08-26). So the duplication this | |
| 14 | + | //! module was written to end is gone by subtraction as well as by description. | |
| 15 | + | //! That is not a reason to inline it back: `project_content.html`'s own copy | |
| 16 | + | //! becomes a described element in its turn, and this is where it goes. | |
| 17 | + | //! | |
| 11 | 18 | //! # What the description says that the markup did not | |
| 12 | 19 | //! | |
| 13 | 20 | //! That the act is destructive, and what to ask before doing it. |
| @@ -236,7 +236,6 @@ | |||
| 236 | 236 | PayoutSummaryPartialTemplate, | |
| 237 | 237 | ProjectSettingsTabTemplate, | |
| 238 | 238 | ProjectCodeTabTemplate, | |
| 239 | - | ProjectBlogTabTemplate, | |
| 240 | 239 | ProjectSubscriptionsTabTemplate, | |
| 241 | 240 | ProjectMembersTabTemplate, | |
| 242 | 241 | ProjectMonetizationTabTemplate, |
| @@ -516,15 +516,6 @@ | |||
| 516 | 516 | pub can_push: bool, | |
| 517 | 517 | } | |
| 518 | 518 | ||
| 519 | - | /// Dashboard blog tab partial. | |
| 520 | - | #[derive(Template)] | |
| 521 | - | #[template(path = "partials/tabs/project_blog.html")] | |
| 522 | - | pub struct ProjectBlogTabTemplate { | |
| 523 | - | pub project_id: String, | |
| 524 | - | pub project_slug: String, | |
| 525 | - | pub posts: Vec<BlogPostDashboardRow>, | |
| 526 | - | } | |
| 527 | - | ||
| 528 | 519 | /// Dashboard subscriptions tab partial for tier management. | |
| 529 | 520 | #[derive(Template)] | |
| 530 | 521 | #[template(path = "partials/tabs/project_subscriptions.html")] |
| @@ -44,7 +44,9 @@ | |||
| 44 | 44 | "content", | |
| 45 | 45 | "analytics", | |
| 46 | 46 | "settings", | |
| 47 | - | "blog", | |
| 47 | + | // "blog" was here until 2026-08-26. The dashboard blog tab was dead | |
| 48 | + | // markup over a route nothing reached, and it went at the | |
| 49 | + | // project_content flip (`6077d0d9`). | |
| 48 | 50 | "subscriptions", | |
| 49 | 51 | ]; | |
| 50 | 52 | for tab in tabs { |
| @@ -138,10 +138,6 @@ | |||
| 138 | 138 | "/dashboard/project/{slug}/tabs/settings", | |
| 139 | 139 | get(project_tabs::project_tab_settings), | |
| 140 | 140 | ) | |
| 141 | - | .route_get( | |
| 142 | - | "/dashboard/project/{slug}/tabs/blog", | |
| 143 | - | get(project_tabs::project_tab_blog), | |
| 144 | - | ) | |
| 145 | 141 | .route_get( | |
| 146 | 142 | "/dashboard/project/{slug}/tabs/monetization", | |
| 147 | 143 | get(project_tabs::project_tab_monetization), |
| @@ -14,11 +14,10 @@ | |||
| 14 | 14 | error::{AppError, Result}, | |
| 15 | 15 | helpers, | |
| 16 | 16 | templates::{ | |
| 17 | - | LinkedRepoView, ProjectAnalyticsTabTemplate, ProjectBlogTabTemplate, | |
| 18 | - | ProjectCodeTabTemplate, ProjectContentTabTemplate, ProjectMembersTabTemplate, | |
| 19 | - | ProjectMonetizationTabTemplate, ProjectOverviewTabTemplate, ProjectPromotionsTabTemplate, | |
| 20 | - | ProjectSettingsTabTemplate, ProjectSubscriptionsTabTemplate, ProjectSyncKitTabTemplate, | |
| 21 | - | RepoCollaboratorView, | |
| 17 | + | LinkedRepoView, ProjectAnalyticsTabTemplate, ProjectCodeTabTemplate, | |
| 18 | + | ProjectContentTabTemplate, ProjectMembersTabTemplate, ProjectMonetizationTabTemplate, | |
| 19 | + | ProjectOverviewTabTemplate, ProjectPromotionsTabTemplate, ProjectSettingsTabTemplate, | |
| 20 | + | ProjectSubscriptionsTabTemplate, ProjectSyncKitTabTemplate, RepoCollaboratorView, | |
| 22 | 21 | }, | |
| 23 | 22 | types::{ | |
| 24 | 23 | BlogPostDashboardRow, ContentItem, Project, ProjectMemberRow, PromoCodeRow, StatCard, | |
| @@ -500,54 +499,6 @@ | |||
| 500 | 499 | )) | |
| 501 | 500 | } | |
| 502 | 501 | ||
| 503 | - | /// Render the HTMX partial for the project blog tab. | |
| 504 | - | #[tracing::instrument(skip_all, name = "project_tabs::project_tab_blog")] | |
| 505 | - | pub(super) async fn project_tab_blog( | |
| 506 | - | State(db): State<PgPool>, | |
| 507 | - | AuthUser(session_user): AuthUser, | |
| 508 | - | headers: HeaderMap, | |
| 509 | - | Path(slug): Path<String>, | |
| 510 | - | ) -> Result<axum::response::Response> { | |
| 511 | - | let (db_project, generation) = | |
| 512 | - | match resolve_project_etag(&db, session_user.id, &slug, &headers).await? { | |
| 513 | - | Ok(pair) => pair, | |
| 514 | - | Err(not_modified) => return Ok(not_modified), | |
| 515 | - | }; | |
| 516 | - | ||
| 517 | - | let db_posts = db::blog_posts::get_blog_posts_by_project(&db, db_project.id).await?; | |
| 518 | - | ||
| 519 | - | let posts: Vec<BlogPostDashboardRow> = db_posts | |
| 520 | - | .into_iter() | |
| 521 | - | .map(|p| BlogPostDashboardRow { | |
| 522 | - | id: p.id.to_string(), | |
| 523 | - | title: p.title, | |
| 524 | - | slug: p.slug.to_string(), | |
| 525 | - | status: if p.published_at.is_some() { | |
| 526 | - | "Published".to_string() | |
| 527 | - | } else { | |
| 528 | - | "Draft".to_string() | |
| 529 | - | }, | |
| 530 | - | status_tone: if p.published_at.is_some() { | |
| 531 | - | crate::types::BadgeStatus::Live.tone() | |
| 532 | - | } else { | |
| 533 | - | crate::types::BadgeStatus::Pending.tone() | |
| 534 | - | }, | |
| 535 | - | published_at: p | |
| 536 | - | .published_at | |
| 537 | - | .map_or_else(|| "-".to_string(), |d| d.format("%b %d, %Y").to_string()), | |
| 538 | - | }) | |
| 539 | - | .collect(); | |
| 540 | - | ||
| 541 | - | Ok(helpers::with_etag( | |
| 542 | - | generation, | |
| 543 | - | ProjectBlogTabTemplate { | |
| 544 | - | project_id: db_project.id.to_string(), | |
| 545 | - | project_slug: db_project.slug.to_string(), | |
| 546 | - | posts, | |
| 547 | - | }, | |
| 548 | - | )) | |
| 549 | - | } | |
| 550 | - | ||
| 551 | 502 | /// Render the HTMX partial for the project promotions tab (promo codes). | |
| 552 | 503 | #[tracing::instrument(skip_all, name = "project_tabs::project_tab_promotions")] | |
| 553 | 504 | pub(super) async fn project_tab_promotions( |
| @@ -1,44 +1,0 @@ | |||
| 1 | - | <div class="tab-docs"><a href="/docs/blog">Docs: Blog →</a></div> | |
| 2 | - | ||
| 3 | - | <div class="content-header"> | |
| 4 | - | <h2 class="subsection-title">Blog Posts</h2> | |
| 5 | - | <a href="/dashboard/project/{{ project_slug }}/blog/new"> | |
| 6 | - | <button class="btn-primary">New Post</button> | |
| 7 | - | </a> | |
| 8 | - | </div> | |
| 9 | - | ||
| 10 | - | {% if posts.is_empty() %} | |
| 11 | - | <div class="empty-state blog-empty-state"> | |
| 12 | - | <p>No blog posts yet. Start writing to engage your audience.</p> | |
| 13 | - | <p class="blog-empty-hint">Share behind-the-scenes updates, release notes, or stories. Posts appear on your project page and in your RSS feed, bringing fans back to your storefront.</p> | |
| 14 | - | </div> | |
| 15 | - | {% else %} | |
| 16 | - | <table class="data-table well"> | |
| 17 | - | <thead> | |
| 18 | - | <tr> | |
| 19 | - | <th class="col-45">Title</th> | |
| 20 | - | <th class="col-15">Status</th> | |
| 21 | - | <th class="col-20">Published</th> | |
| 22 | - | <th class="col-20">Actions</th> | |
| 23 | - | </tr> | |
| 24 | - | </thead> | |
| 25 | - | <tbody> | |
| 26 | - | {% for post in posts %} | |
| 27 | - | <tr id="post-row-{{ post.id }}"> | |
| 28 | - | <td> | |
| 29 | - | <a href="/p/{{ project_slug }}/blog/{{ post.slug }}" class="blog-post-title-link">{{ post.title }}</a> | |
| 30 | - | </td> | |
| 31 | - | <td><span class="badge" data-tone="{{ post.status_tone }}">{{ post.status }}</span></td> | |
| 32 | - | <td>{{ post.published_at }}</td> | |
| 33 | - | <td> | |
| 34 | - | <div class="blog-post-actions"> | |
| 35 | - | <a href="/p/{{ project_slug }}/blog/{{ post.slug }}" class="btn-secondary small">View</a> | |
| 36 | - | <a href="/dashboard/project/{{ project_slug }}/blog/new?post={{ post.id }}" class="btn-secondary small">Edit</a> | |
| 37 | - | {{ crate::quasi::blog_delete_act::html(post.id.to_string().as_str())|safe }} | |
| 38 | - | </div> | |
| 39 | - | </td> | |
| 40 | - | </tr> | |
| 41 | - | {% endfor %} | |
| 42 | - | </tbody> | |
| 43 | - | </table> | |
| 44 | - | {% endif %} |