feat(i18n): add missing translation keys to TRANSLATION_KEYS array

- Add people-* keys (title, subtitle, search, tabs, form fields)
- Add crm-* keys (stages, stats, metrics)
- Add billing-* keys (subtitle, new-payment, revenue metrics)
- Add products-* keys (subtitle, items, stats)
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-01-12 14:13:35 -03:00
parent fd03a324b9
commit 4ed05f3f19
4 changed files with 148 additions and 12 deletions

View file

@ -172,14 +172,6 @@ date.with_hour(9).and_then(|d| d.with_minute(0)).unwrap_or(date)
---
## BUILD RULES
- Development: `cargo build` (debug only)
- NEVER run `cargo clippy` manually - use diagnostics tool
- Version: 6.1.0 - do not change
---
## DATABASE STANDARDS
- TABLES AND INDEXES ONLY (no views, triggers, functions)
@ -210,6 +202,34 @@ date.with_hour(9).and_then(|d| d.with_minute(0)).unwrap_or(date)
---
## COMPILATION POLICY - CRITICAL
**NEVER compile during development. NEVER run `cargo build`. Use static analysis only.**
### Workflow
1. Make all code changes
2. Use `diagnostics` tool for static analysis (NOT compilation)
3. Fix any errors found by diagnostics
4. **At the end**, inform user what needs restart
### After All Changes Complete
| Change Type | User Action Required |
|-------------|----------------------|
| Rust code (`.rs` files) | "Recompile and restart **botserver**" |
| HTML templates (`.html` in botui) | "Browser refresh only" |
| CSS/JS files | "Browser refresh only" |
| Askama templates (`.html` in botserver) | "Recompile and restart **botserver**" |
| Database migrations | "Run migration, then restart **botserver**" |
| Cargo.toml changes | "Recompile and restart **botserver**" |
**Format:** At the end of your response, always state:
- ✅ **No restart needed** - browser refresh only
- 🔄 **Restart botserver** - recompile required
---
## KEY REMINDERS
- **ZERO WARNINGS** - fix every clippy warning

View file

@ -808,6 +808,74 @@ const TRANSLATION_KEYS: &[&str] = &[
"attendant-status-online",
"attendant-select-conversation",
"sources-search",
"people-title",
"people-subtitle",
"people-search",
"people-add",
"people-tab-contacts",
"people-tab-groups",
"people-tab-directory",
"people-tab-recent",
"people-loading",
"people-add-contact",
"people-first-name",
"people-last-name",
"people-email",
"people-phone",
"people-company",
"people-job-title",
"people-notes",
"people-empty-title",
"people-empty-desc",
"crm-title",
"crm-subtitle",
"crm-leads",
"crm-opportunities",
"crm-contacts",
"crm-accounts",
"crm-pipeline",
"crm-new-lead",
"crm-new-opportunity",
"crm-new-contact",
"crm-new-account",
"crm-stage-lead",
"crm-stage-qualified",
"crm-stage-proposal",
"crm-stage-negotiation",
"crm-stage-won",
"crm-stage-lost",
"crm-conversion-rate",
"crm-pipeline-value",
"crm-avg-deal",
"crm-won-month",
"billing-title",
"billing-subtitle",
"billing-invoices",
"billing-payments",
"billing-quotes",
"billing-new-invoice",
"billing-new-payment",
"billing-new-quote",
"billing-pending",
"billing-paid",
"billing-overdue",
"billing-revenue-month",
"billing-total-revenue",
"products-title",
"products-subtitle",
"products-items",
"products-services",
"products-pricelists",
"products-new-product",
"products-new-service",
"products-new-pricelist",
"products-total-products",
"products-total-services",
"products-active",
"products-inactive",
"products-sku",
"products-price",
"products-category",
];
pub fn get_translations_json(locale: &Locale) -> serde_json::Value {

View file

@ -383,6 +383,7 @@ async fn run_axum_server(
api_router = api_router.merge(botserver::analytics::goals::configure_goals_routes());
api_router = api_router.merge(botserver::player::configure_player_routes());
api_router = api_router.merge(botserver::canvas::configure_canvas_routes());
api_router = api_router.merge(botserver::social::configure_social_routes());
#[cfg(feature = "whatsapp")]
{

View file

@ -1,5 +1,5 @@
use axum::{
extract::{Path, Query, State},
extract::{Form, Path, Query, State},
response::{Html, IntoResponse},
routing::{delete, get, post, put},
Json, Router,
@ -293,6 +293,13 @@ pub struct CreatePostRequest {
pub poll: Option<CreatePollRequest>,
}
#[derive(Debug, Deserialize)]
pub struct CreatePostForm {
pub content: String,
pub visibility: Option<String>,
pub community_id: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct AttachmentRequest {
pub file_type: AttachmentType,
@ -855,13 +862,53 @@ pub async fn handle_get_feed_html(
pub async fn handle_create_post(
State(_state): State<Arc<AppState>>,
Json(req): Json<CreatePostRequest>,
) -> Result<Json<Post>, SocialError> {
Form(form): Form<CreatePostForm>,
) -> Result<Html<String>, SocialError> {
let service = SocialService::new();
let org_id = Uuid::nil();
let user_id = Uuid::nil();
let visibility = form.visibility.as_deref().and_then(|v| match v {
"public" => Some(PostVisibility::Public),
"organization" => Some(PostVisibility::Organization),
"community" => Some(PostVisibility::Community),
_ => None,
});
let community_id = form
.community_id
.as_deref()
.filter(|s| !s.is_empty())
.and_then(|s| Uuid::parse_str(s).ok());
let req = CreatePostRequest {
content: form.content,
content_type: None,
community_id,
visibility,
mentions: None,
hashtags: None,
attachments: None,
poll: None,
};
let post = service.create_post(org_id, user_id, req).await?;
Ok(Json(post))
let post_with_author = PostWithAuthor {
post,
author: UserSummary {
id: user_id,
name: "You".to_string(),
avatar_url: None,
title: None,
is_leader: false,
},
community: None,
user_reaction: None,
is_bookmarked: false,
};
Ok(Html(render_post_card_html(&post_with_author)))
}
pub async fn handle_get_post(