feat: Implement dark mode styling across UI components and update README with roadmap and system requirements.

This commit is contained in:
2026-03-02 11:29:55 -03:00
parent 8c6cba6d2e
commit 81e1830563
15 changed files with 555 additions and 374 deletions
+93 -14
View File
@@ -1721,6 +1721,7 @@ pub struct AdminCreateUserPayload {
pub password: String,
pub full_name: String,
pub role: String,
pub organization_id: Option<Uuid>,
}
pub async fn register(
@@ -1809,6 +1810,15 @@ pub async fn admin_create_user(
let password_hash = hash(payload.password, DEFAULT_COST)
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Hashing failed".into()))?;
let is_super_admin = claims.role == "admin"
&& claims.org == Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
let target_org_id = if is_super_admin {
payload.organization_id.unwrap_or(org_ctx.id)
} else {
org_ctx.id
};
let user = sqlx::query_as::<_, User>(
"INSERT INTO users (email, password_hash, full_name, role, organization_id) VALUES ($1, $2, $3, $4, $5) RETURNING *"
)
@@ -1816,7 +1826,7 @@ pub async fn admin_create_user(
.bind(password_hash)
.bind(&payload.full_name)
.bind(&payload.role)
.bind(org_ctx.id)
.bind(target_org_id)
.fetch_one(&pool)
.await
.map_err(|e| {
@@ -2662,20 +2672,51 @@ pub async fn update_user(
.get("organization_id")
.and_then(|o| o.as_str())
.and_then(|o| Uuid::parse_str(o).ok());
let is_super_admin = claims.role == "admin"
&& claims.org == Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
let user = sqlx::query_as::<_, User>(
"UPDATE users SET role = COALESCE($1, role), organization_id = COALESCE($2, organization_id), full_name = COALESCE($3, full_name), avatar_url = COALESCE($4, avatar_url), bio = COALESCE($5, bio), language = COALESCE($6, language) WHERE id = $7 AND organization_id = $8 RETURNING *"
)
.bind(role)
.bind(organization_id)
.bind(full_name)
.bind(avatar_url)
.bind(bio)
.bind(language)
.bind(id)
.bind(org_ctx.id)
.fetch_one(&pool)
.await
let user = if is_super_admin {
sqlx::query_as::<_, User>(
"UPDATE users SET
role = COALESCE($1, role),
organization_id = COALESCE($2, organization_id),
full_name = COALESCE($3, full_name),
avatar_url = COALESCE($4, avatar_url),
bio = COALESCE($5, bio),
language = COALESCE($6, language)
WHERE id = $7 RETURNING *"
)
.bind(role)
.bind(organization_id)
.bind(full_name)
.bind(avatar_url)
.bind(bio)
.bind(language)
.bind(id)
.fetch_one(&pool)
.await
} else {
sqlx::query_as::<_, User>(
"UPDATE users SET
role = COALESCE($1, role),
organization_id = COALESCE($2, organization_id),
full_name = COALESCE($3, full_name),
avatar_url = COALESCE($4, avatar_url),
bio = COALESCE($5, bio),
language = COALESCE($6, language)
WHERE id = $7 AND organization_id = $8 RETURNING *"
)
.bind(role)
.bind(organization_id)
.bind(full_name)
.bind(avatar_url)
.bind(bio)
.bind(language)
.bind(id)
.bind(org_ctx.id)
.fetch_one(&pool)
.await
}
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
log_action(
@@ -2790,6 +2831,44 @@ pub async fn create_organization(
Ok(Json(org))
}
pub async fn update_organization(
claims: common::auth::Claims,
State(pool): State<PgPool>,
Path(id): Path<Uuid>,
Json(payload): Json<serde_json::Value>,
) -> Result<Json<Organization>, (StatusCode, String)> {
// Only super admins or admins of the same org?
// Usually editing other orgs is a Super Admin only task.
let is_super_admin = claims.role == "admin"
&& claims.org == Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
if !is_super_admin {
return Err((StatusCode::FORBIDDEN, "Super Admin access required".into()));
}
let name = payload.get("name").and_then(|v| v.as_str());
let domain = payload.get("domain").and_then(|v| v.as_str());
let org = sqlx::query_as::<_, Organization>(
"UPDATE organizations SET
name = COALESCE($1, name),
domain = COALESCE($2, domain),
updated_at = NOW()
WHERE id = $3 RETURNING *"
)
.bind(name)
.bind(domain)
.bind(id)
.fetch_one(&pool)
.await
.map_err(|e| {
tracing::error!("Failed to update organization: {}", e);
(StatusCode::INTERNAL_SERVER_ERROR, "Failed to update organization".into())
})?;
Ok(Json(org))
}
pub async fn provision_organization(
claims: common::auth::Claims,
State(pool): State<PgPool>,
+1
View File
@@ -171,6 +171,7 @@ async fn main() {
"/organizations",
get(handlers::get_organizations).post(handlers::create_organization),
)
.route("/organizations/{id}", put(handlers::update_organization))
.route("/admin/provision", post(handlers::provision_organization))
.route(
"/webhooks",