1use crate::forms::base::{CommonFieldConfig, FieldConfig, FormField};
3use crate::utils::aliases::ATera;
4use crate::utils::trad::{t, tf};
5use async_trait::async_trait;
6use serde::Serialize;
7use serde_json::{Value, json};
8
9#[derive(Clone, Debug, Serialize)]
11pub struct ChoiceOption {
12 pub value: String,
13 pub label: String,
14 pub selected: bool,
15}
16
17impl ChoiceOption {
18 pub fn new(value: &str, label: &str) -> Self {
20 Self {
21 value: value.to_string(),
22 label: label.to_string(),
23 selected: false,
24 }
25 }
26
27 pub fn selected(mut self) -> Self {
29 self.selected = true;
30 self
31 }
32}
33
34#[derive(Clone, Serialize, Debug)]
36pub struct ChoiceField {
37 pub base: FieldConfig,
38 pub choices: Vec<ChoiceOption>,
39 pub multiple: bool,
40}
41
42impl ChoiceField {
43 pub fn new(name: &str) -> Self {
45 Self {
46 base: FieldConfig::new(name, "select", "base_select.html"),
47 choices: Vec::new(),
48 multiple: false,
49 }
50 }
51
52 pub fn multiple(mut self) -> Self {
54 self.multiple = true;
55 self.base.type_field = "select-multiple".to_string();
56 self
57 }
58
59 pub fn choices(mut self, choices: Vec<ChoiceOption>) -> Self {
61 self.choices = choices;
62 self
63 }
64
65 pub fn add_choice(mut self, value: &str, label: &str) -> Self {
67 self.choices.push(ChoiceOption::new(value, label));
68 self
69 }
70
71 pub fn label(mut self, label: &str) -> Self {
73 self.base.label = label.to_string();
74 self
75 }
76
77 pub fn required(mut self) -> Self {
79 self.set_required(true, None);
80 self
81 }
82}
83
84impl CommonFieldConfig for ChoiceField {
85 fn get_field_config(&self) -> &FieldConfig {
86 &self.base
87 }
88
89 fn get_field_config_mut(&mut self) -> &mut FieldConfig {
90 &mut self.base
91 }
92}
93
94#[async_trait]
95impl FormField for ChoiceField {
96 fn set_value(&mut self, value: &str) {
99 self.base.value = value.to_string();
100 if self.multiple {
101 let selected: Vec<&str> = value.split(',').map(str::trim).collect();
102 for choice in &mut self.choices {
103 choice.selected = selected.contains(&choice.value.as_str());
104 }
105 }
106 }
107
108 async fn validate(&mut self) -> bool {
109 let val = self.base.value.trim();
110
111 if self.base.is_required.choice && val.is_empty() {
112 let msg = self
113 .base
114 .is_required
115 .message
116 .clone()
117 .unwrap_or_else(|| t("forms.select_required").to_string());
118 self.set_error(msg);
119 return false;
120 }
121
122 if !val.is_empty() && self.multiple {
123 for selected in val.split(',').map(str::trim) {
125 if !self.choices.iter().any(|c| c.value == selected) {
126 self.set_error(tf("forms.choice_invalid_value", &[selected]));
127 return false;
128 }
129 }
130 } else if !val.is_empty() {
131 let valid = self.choices.iter().any(|c| c.value == val);
133 if !valid {
134 self.set_error(t("forms.choice_invalid").to_string());
135 return false;
136 }
137 }
138
139 self.clear_error();
140 true
141 }
142
143 fn render(&self, tera: &ATera) -> Result<String, String> {
144 let mut context = self.base_context();
145 context.insert("choices", &self.choices);
146 context.insert("multiple", &self.multiple);
147
148 tera.render(&self.base.template_name, &context)
149 .map_err(|e| {
150 tf(
151 "forms.finalize_error",
152 &[&self.base.template_name, &e.to_string()],
153 )
154 .to_string()
155 })
156 }
157}
158
159#[derive(Clone, Serialize, Debug)]
161pub struct RadioField {
162 pub base: FieldConfig,
163 pub choices: Vec<ChoiceOption>,
164}
165
166impl RadioField {
167 pub fn new(name: &str) -> Self {
169 Self {
170 base: FieldConfig::new(name, "radio", "base_radio.html"),
171 choices: Vec::new(),
172 }
173 }
174
175 pub fn choices(mut self, choices: Vec<ChoiceOption>) -> Self {
177 self.choices = choices;
178 self
179 }
180
181 pub fn add_choice(mut self, value: &str, label: &str) -> Self {
183 self.choices.push(ChoiceOption::new(value, label));
184 self
185 }
186
187 pub fn label(mut self, label: &str) -> Self {
189 self.base.label = label.to_string();
190 self
191 }
192
193 pub fn required(mut self) -> Self {
195 self.set_required(true, None);
196 self
197 }
198}
199
200impl CommonFieldConfig for RadioField {
201 fn get_field_config(&self) -> &FieldConfig {
202 &self.base
203 }
204
205 fn get_field_config_mut(&mut self) -> &mut FieldConfig {
206 &mut self.base
207 }
208}
209
210#[async_trait]
211impl FormField for RadioField {
212 async fn validate(&mut self) -> bool {
213 let val = self.base.value.trim();
214
215 if self.base.is_required.choice && val.is_empty() {
216 let msg = self
217 .base
218 .is_required
219 .message
220 .clone()
221 .unwrap_or_else(|| t("forms.select_required").to_string());
222 self.set_error(msg);
223 return false;
224 }
225
226 if !val.is_empty() {
227 let valid = self.choices.iter().any(|c| c.value == val);
228 if !valid {
229 self.set_error(t("forms.choice_invalid").to_string());
230 return false;
231 }
232 }
233
234 self.clear_error();
235 true
236 }
237
238 fn render(&self, tera: &ATera) -> Result<String, String> {
239 let mut context = self.base_context();
240 context.insert("choices", &self.choices);
241 context.insert("meta", &self.to_json_meta());
242 tera.render(&self.base.template_name, &context)
243 .map_err(|e| {
244 tf(
245 "forms.finalize_error",
246 &[&self.base.template_name, &e.to_string()],
247 )
248 .to_string()
249 })
250 }
251
252 fn to_json_value(&self) -> Value {
253 json!(self.base.value)
254 }
255
256 fn to_json_required(&self) -> Value {
257 json!(self.base.is_required)
258 }
259
260 fn to_json_attributes(&self) -> Value {
261 json!(self.base.html_attributes)
262 }
263}
264
265#[derive(Clone, Serialize, Debug)]
267pub struct CheckboxField {
268 pub base: FieldConfig,
269 pub choices: Vec<ChoiceOption>,
270}
271
272impl CheckboxField {
273 pub fn new(name: &str) -> Self {
275 Self {
276 base: FieldConfig::new(name, "checkbox", "base_checkbox.html"),
277 choices: Vec::new(),
278 }
279 }
280
281 pub fn choices(mut self, choices: Vec<ChoiceOption>) -> Self {
283 self.choices = choices;
284 self
285 }
286
287 pub fn add_choice(mut self, value: &str, label: &str) -> Self {
289 self.choices.push(ChoiceOption::new(value, label));
290 self
291 }
292
293 pub fn label(mut self, label: &str) -> Self {
295 self.base.label = label.to_string();
296 self
297 }
298
299 pub fn required(mut self) -> Self {
301 self.set_required(true, None);
302 self
303 }
304}
305impl CommonFieldConfig for CheckboxField {
306 fn get_field_config(&self) -> &FieldConfig {
307 &self.base
308 }
309
310 fn get_field_config_mut(&mut self) -> &mut FieldConfig {
311 &mut self.base
312 }
313}
314
315#[async_trait]
316impl FormField for CheckboxField {
317 fn set_value(&mut self, value: &str) {
318 self.base.value = value.to_string();
320 let selected_values: Vec<&str> = value.split(',').map(|s| s.trim()).collect();
321
322 for choice in &mut self.choices {
323 choice.selected = selected_values.contains(&choice.value.as_str());
324 }
325 }
326
327 async fn validate(&mut self) -> bool {
328 let val = self.base.value.trim();
329
330 if self.base.is_required.choice && val.is_empty() {
331 let msg = self
332 .base
333 .is_required
334 .message
335 .clone()
336 .unwrap_or_else(|| t("forms.checkbox_required").to_string());
337 self.set_error(msg);
338 return false;
339 }
340
341 if !val.is_empty() {
342 let selected_values: Vec<&str> = val.split(',').map(|s| s.trim()).collect();
344 for sel_val in selected_values {
345 if !self.choices.iter().any(|c| c.value == sel_val) {
346 self.set_error(tf("forms.choice_invalid_value", &[sel_val]));
347 return false;
348 }
349 }
350 }
351
352 self.set_error("".into());
353 true
354 }
355
356 fn render(&self, tera: &ATera) -> Result<String, String> {
357 let mut context = self.base_context();
358 context.insert("choices", &self.choices);
359 context.insert("meta", &self.to_json_meta());
360
361 tera.render(&self.base.template_name, &context)
362 .map_err(|e| {
363 tf(
364 "forms.finalize_error",
365 &[&self.base.template_name, &e.to_string()],
366 )
367 .to_string()
368 })
369 }
370}