Skip to main content

reinhardt_admin/server/
fields.rs

1//! Field definitions Server Function
2//!
3//! Provides field information for dynamic form generation.
4
5use crate::adapters::{AdminDatabase, AdminRecord, AdminSite, FieldInfo, FieldType};
6use crate::types::FieldsResponse;
7use reinhardt_pages::server_fn::{ServerFnError, server_fn};
8use std::sync::Arc;
9
10#[cfg(not(target_arch = "wasm32"))]
11use super::error::MapServerFnError;
12#[cfg(not(target_arch = "wasm32"))]
13use crate::server::type_inference::{get_field_metadata, infer_admin_field_type, infer_required};
14#[cfg(not(target_arch = "wasm32"))]
15use reinhardt_utils::utils_core::text::humanize_field_name;
16
17/// Get field definitions for dynamic form generation
18///
19/// Retrieves field metadata for creating or editing model instances.
20/// When `id` is provided, also retrieves the existing field values for editing.
21///
22/// # Server Function
23///
24/// This function is automatically exposed as an HTTP endpoint by the `#[server_fn]` macro.
25/// AdminSite and AdminDatabase dependencies are automatically injected via the DI system.
26///
27/// # Example
28///
29/// ```ignore
30/// use reinhardt_admin::server::get_fields;
31///
32/// // Client-side usage for create form
33/// let response = get_fields("User".to_string(), None).await?;
34/// println!("Fields: {:?}", response.fields);
35///
36/// // Client-side usage for edit form
37/// let response = get_fields("User".to_string(), Some("42".to_string())).await?;
38/// println!("Existing values: {:?}", response.values);
39/// ```
40#[server_fn(use_inject = true)]
41pub async fn get_fields(
42	model_name: String,
43	id: Option<String>,
44	#[inject] site: Arc<AdminSite>,
45	#[inject] db: Arc<AdminDatabase>,
46) -> Result<FieldsResponse, ServerFnError> {
47	let model_admin = site.get_model_admin(&model_name).map_server_fn_error()?;
48	let field_names = model_admin
49		.fields()
50		.unwrap_or_else(|| model_admin.list_display());
51	let readonly_fields = model_admin.readonly_fields();
52
53	// Build field metadata with type inference from global registry
54	let table_name = model_admin.table_name();
55	let fields = field_names
56		.iter()
57		.map(|&name| {
58			let is_readonly = readonly_fields.contains(&name);
59
60			// Try to get field metadata from the global model registry
61			let (field_type, required) = get_field_metadata(table_name, name)
62				.map(|meta| {
63					let admin_type = infer_admin_field_type(&meta.field_type);
64					let is_required = infer_required(&meta);
65					(admin_type, is_required)
66				})
67				.unwrap_or_else(|| (FieldType::Text, false));
68
69			FieldInfo {
70				name: name.to_string(),
71				label: humanize_field_name(name),
72				field_type,
73				required,
74				readonly: is_readonly,
75				help_text: None,
76				placeholder: None,
77			}
78		})
79		.collect();
80
81	// Fetch existing values if editing
82	let values = if let Some(id) = id {
83		db.get::<AdminRecord>(model_admin.table_name(), model_admin.pk_field(), &id)
84			.await
85			.map_server_fn_error()?
86	} else {
87		None
88	};
89
90	Ok(FieldsResponse {
91		model_name,
92		fields,
93		values,
94	})
95}