Skip to main content

reinhardt_admin/server/
fields.rs

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