Skip to content

Commit 9fc8101

Browse files
committed
update: modal required field error messages
1 parent bb500ef commit 9fc8101

8 files changed

Lines changed: 159 additions & 73 deletions

File tree

fastapi_forge/frontend/modals/enum_modal.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from nicegui import ui
55

66
from fastapi_forge.dtos import CustomEnumValue
7+
from fastapi_forge.frontend.notifications import notify_value_error
78
from fastapi_forge.frontend.state import state
89

910

@@ -55,11 +56,19 @@ def _build_action_buttons(self) -> None:
5556
ui.button("Cancel", on_click=self.close)
5657
ui.button(
5758
self.title,
58-
on_click=lambda: self.on_add_value(
59+
on_click=self._handle_add,
60+
)
61+
62+
def _handle_add(self) -> None:
63+
try:
64+
self.on_add_value(
5965
name=self.value_name.value,
6066
value=self.value_value.value,
61-
),
62-
)
67+
)
68+
self.close()
69+
except ValueError as exc:
70+
notify_value_error(exc)
71+
return
6372

6473

6574
class UpdateEnumValueModal(BaseEnumValueModal):
@@ -79,12 +88,15 @@ def _build_action_buttons(self) -> None:
7988
def _handle_update(self) -> None:
8089
if not state.selected_enum_value:
8190
return
82-
83-
self.on_update_value(
84-
name=self.value_name.value,
85-
value=self.value_value.value,
86-
)
87-
self.close()
91+
try:
92+
self.on_update_value(
93+
name=self.value_name.value,
94+
value=self.value_value.value,
95+
)
96+
self.close()
97+
except ValueError as exc:
98+
notify_value_error(exc)
99+
return
88100

89101
def _set_value(self, enum_value: CustomEnumValue) -> None:
90102
state.selected_enum_value = enum_value

fastapi_forge/frontend/modals/field_modal.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
from fastapi_forge.dtos import ModelField, ModelFieldMetadata
88
from fastapi_forge.enums import FieldDataTypeEnum
9-
from fastapi_forge.frontend.notifications import notify_validation_error
9+
from fastapi_forge.frontend.notifications import (
10+
notify_validation_error,
11+
notify_value_error,
12+
)
1013
from fastapi_forge.frontend.state import state
1114
from fastapi_forge.jinja_utils import generate_field
1215

@@ -298,43 +301,47 @@ def _handle_update(self) -> None:
298301
if not state.selected_field:
299302
return
300303

301-
self.on_update_field(
302-
name=self.field_name.value,
303-
type=self.field_type.value,
304-
type_enum=(
305-
next(
306-
(
307-
e.name
308-
for e in state.custom_enums
309-
if e.name == self.enum_selector.value
310-
),
311-
None,
312-
)
313-
if self.show_enum_selector
314-
else None
315-
),
316-
primary_key=self.primary_key.value,
317-
nullable=self.nullable.value,
318-
unique=self.unique.value,
319-
index=self.index.value,
320-
default_value=(
321-
self.default_value_select.value
322-
if self.show_enum_defaults
323-
else self.default_value_input.value
324-
)
325-
or None,
326-
extra_kwargs=self.extra_kwargs or None,
327-
metadata=ModelFieldMetadata(
328-
is_created_at_timestamp=(
329-
self.created_at.value if self.show_metadata else False
304+
try:
305+
self.on_update_field(
306+
name=self.field_name.value,
307+
type=self.field_type.value,
308+
type_enum=(
309+
next(
310+
(
311+
e.name
312+
for e in state.custom_enums
313+
if e.name == self.enum_selector.value
314+
),
315+
None,
316+
)
317+
if self.show_enum_selector
318+
else None
330319
),
331-
is_updated_at_timestamp=(
332-
self.updated_at.value if self.show_metadata else False
320+
primary_key=self.primary_key.value,
321+
nullable=self.nullable.value,
322+
unique=self.unique.value,
323+
index=self.index.value,
324+
default_value=(
325+
self.default_value_select.value
326+
if self.show_enum_defaults
327+
else self.default_value_input.value
328+
)
329+
or None,
330+
extra_kwargs=self.extra_kwargs or None,
331+
metadata=ModelFieldMetadata(
332+
is_created_at_timestamp=(
333+
self.created_at.value if self.show_metadata else False
334+
),
335+
is_updated_at_timestamp=(
336+
self.updated_at.value if self.show_metadata else False
337+
),
338+
is_foreign_key=False,
333339
),
334-
is_foreign_key=False,
335-
),
336-
)
337-
self.close()
340+
)
341+
self.close()
342+
except ValueError as exc:
343+
notify_value_error(exc)
344+
return
338345

339346
def _set_field(self, field: ModelField) -> None:
340347
state.selected_field = field

fastapi_forge/frontend/modals/relation_modal.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from fastapi_forge.dtos import Model, ModelRelationship
77
from fastapi_forge.enums import OnDeleteEnum
8+
from fastapi_forge.frontend.notifications import notify_value_error
89

910

1011
class BaseRelationModal(ui.dialog, ABC):
@@ -74,16 +75,19 @@ def _build_action_buttons(self) -> None:
7475
)
7576

7677
def _add_relation(self) -> None:
77-
self.on_add_relation(
78-
field_name=self.field_name.value,
79-
target_model=self.target_model.value,
80-
back_populates=self.back_populates.value or None,
81-
nullable=self.nullable.value,
82-
index=self.index.value,
83-
unique=self.unique.value,
84-
on_delete=self.on_delete.value,
85-
)
86-
self.close()
78+
try:
79+
self.on_add_relation(
80+
field_name=self.field_name.value,
81+
target_model=self.target_model.value,
82+
back_populates=self.back_populates.value or None,
83+
nullable=self.nullable.value,
84+
index=self.index.value,
85+
unique=self.unique.value,
86+
on_delete=self.on_delete.value,
87+
)
88+
self.close()
89+
except ValueError as exc:
90+
notify_value_error(exc)
8791

8892
def open(self, models: list[Model]) -> None:
8993
self.target_model.options = [model.name for model in models]
@@ -110,16 +114,19 @@ def _update_relation(self) -> None:
110114
if not self.selected_relation:
111115
return
112116

113-
self.on_update_relation(
114-
field_name=self.field_name.value,
115-
target_model=self.target_model.value,
116-
back_populates=self.back_populates.value,
117-
nullable=self.nullable.value,
118-
index=self.index.value,
119-
unique=self.unique.value,
120-
on_delete=self.on_delete.value,
121-
)
122-
self.close()
117+
try:
118+
self.on_update_relation(
119+
field_name=self.field_name.value,
120+
target_model=self.target_model.value,
121+
back_populates=self.back_populates.value,
122+
nullable=self.nullable.value,
123+
index=self.index.value,
124+
unique=self.unique.value,
125+
on_delete=self.on_delete.value,
126+
)
127+
self.close()
128+
except ValueError as exc:
129+
notify_value_error(exc)
123130

124131
def _set_relation(self, relation: ModelRelationship) -> None:
125132
self.selected_relation = relation

fastapi_forge/frontend/notifications.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ def notify_validation_error(e: ValidationError) -> None:
77
ui.notify(msg, type="negative")
88

99

10+
def notify_value_error(e: ValueError) -> None:
11+
ui.notify(str(e), type="negative")
12+
13+
1014
def notify_model_exists(model_name: str) -> None:
1115
ui.notify(
1216
f"Model '{model_name}' already exists.",

fastapi_forge/frontend/panels/enum_editor_panel.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic import ValidationError
55

66
from fastapi_forge.dtos import CustomEnum, CustomEnumValue
7+
from fastapi_forge.frontend import validation
78
from fastapi_forge.frontend.constants import ENUM_COLUMNS
89
from fastapi_forge.frontend.modals import (
910
AddEnumValueModal,
@@ -116,6 +117,11 @@ def _handle_modal_add_value(self, *, name: str, value: str) -> None:
116117
if state.selected_enum is None:
117118
return
118119

120+
try:
121+
validation.raise_if_missing_fields([("Name", name), ("Value", value)])
122+
except ValueError as exc:
123+
raise exc
124+
119125
if self._enum_value_exists(name):
120126
notify_enum_value_exists(name, state.selected_enum.name)
121127
return
@@ -135,6 +141,11 @@ def _handle_update_value(self, *, name: str, value: str) -> None:
135141
if state.selected_enum is None or state.selected_enum_value is None:
136142
return
137143

144+
try:
145+
validation.raise_if_missing_fields([("Name", name), ("Value", value)])
146+
except ValueError as exc:
147+
raise exc
148+
138149
if self._enum_value_exists(name):
139150
notify_enum_value_exists(name, state.selected_enum.name)
140151
return

fastapi_forge/frontend/panels/model_editor_panel.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ModelRelationship,
1111
)
1212
from fastapi_forge.enums import FieldDataTypeEnum, OnDeleteEnum
13+
from fastapi_forge.frontend import validation
1314
from fastapi_forge.frontend.constants import (
1415
DEFAULT_AUTH_USER_FIELDS,
1516
FIELD_COLUMNS,
@@ -24,6 +25,7 @@
2425
from fastapi_forge.frontend.notifications import (
2526
notify_field_exists,
2627
notify_validation_error,
28+
notify_value_error,
2729
)
2830
from fastapi_forge.frontend.state import state
2931
from fastapi_forge.jinja import render_model_to_model
@@ -309,21 +311,32 @@ def _handle_modal_add_field(
309311
self._add_field(**kwargs)
310312
self.add_field_modal.close()
311313
except ValueError as exc:
312-
ui.notify(str(exc), type="negative")
314+
notify_value_error(exc)
313315

314316
def _handle_modal_add_relation(
315317
self,
316318
field_name: str,
317319
target_model: str,
320+
on_delete: OnDeleteEnum,
318321
nullable: bool,
319322
index: bool,
320323
unique: bool,
321-
on_delete: OnDeleteEnum,
322324
back_populates: str | None = None,
323325
) -> None:
324326
if not state.selected_model:
325327
return
326328

329+
try:
330+
validation.raise_if_missing_fields(
331+
[
332+
("Field Name", field_name),
333+
("Target Model", target_model),
334+
("On Delete", on_delete),
335+
]
336+
)
337+
except ValueError as exc:
338+
raise exc
339+
327340
target_model_instance = next(
328341
(model for model in state.models if model.name == target_model),
329342
None,
@@ -408,6 +421,13 @@ def _add_field(
408421
if state.selected_model is None:
409422
return
410423

424+
try:
425+
validation.raise_if_missing_fields(
426+
[("Field Name", name), ("Field Type", type)]
427+
)
428+
except ValueError as exc:
429+
raise exc
430+
411431
if self._field_name_exists(name):
412432
notify_field_exists(name, state.selected_model.name)
413433
return
@@ -519,6 +539,13 @@ def _handle_update_field(
519539
if name in exclude_set:
520540
return
521541

542+
try:
543+
validation.raise_if_missing_fields(
544+
[("Field Name", name), ("Field Type", type)]
545+
)
546+
except ValueError as exc:
547+
raise exc
548+
522549
if state.selected_field.name != name and self._field_name_exists(name):
523550
notify_field_exists(name, state.selected_model.name)
524551
return
@@ -561,6 +588,17 @@ def _handle_update_relation(
561588
if not state.selected_model or not state.selected_relation:
562589
return
563590

591+
try:
592+
validation.raise_if_missing_fields(
593+
[
594+
("Field Name", field_name),
595+
("Target Model", target_model),
596+
("On Delete", on_delete),
597+
]
598+
)
599+
except ValueError as exc:
600+
raise exc
601+
564602
target_model_instance = next(
565603
(model for model in state.models if model.name == target_model),
566604
None,

fastapi_forge/frontend/panels/project_config_panel.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,10 @@ def _update_taskiq_state(self, *_) -> None:
162162
self.use_taskiq.value = False
163163
state.use_taskiq = False
164164

165-
async def _create_auth_model_dialog(self) -> bool:
165+
async def _auth_dialog(self) -> bool:
166166
dialog = ui.dialog()
167167
with dialog, ui.card().classes("w-full max-w-md p-6 text-center"):
168-
ui.icon("check_circle", color="green-500").classes("text-4xl self-center")
168+
ui.icon("security", color="green-500").classes("text-4xl self-center")
169169
ui.markdown(
170170
"JWT Auth needs a model with 'email' and 'password' fields. "
171171
"Select any model as auth model later, or create one now (optional)."
@@ -176,9 +176,9 @@ async def _create_auth_model_dialog(self) -> bool:
176176
).classes("w-full mt-4")
177177

178178
with ui.row().classes("w-full justify-center gap-4 mt-4"):
179-
ui.button("Cancel", color="negative", on_click=dialog.close)
179+
ui.button("Close", color="negative", on_click=dialog.close)
180180
ui.button(
181-
"Enable JWT Auth",
181+
"Proceed",
182182
color="primary",
183183
on_click=lambda: dialog.submit(create_model_checkbox.value),
184184
)
@@ -203,9 +203,9 @@ async def _handle_builtin_auth_change(
203203
state.render_actions_fn.refresh()
204204
return
205205

206-
create_model_dialog = await self._create_auth_model_dialog()
206+
proceed = await self._auth_dialog()
207207

208-
if not create_model_dialog:
208+
if not proceed:
209209
return
210210

211211
if any(model.name == "auth_user" for model in state.models):

0 commit comments

Comments
 (0)