Skip to content

Commit

Permalink
frontend: add success alert
Browse files Browse the repository at this point in the history
  • Loading branch information
hynseok committed Nov 4, 2024
1 parent 469e1ca commit 05671f5
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 67 deletions.
176 changes: 109 additions & 67 deletions frontend/src/components/form.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use serde::Serialize;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::window;
use yew::format::{Json, Nothing};
use yew::prelude::*;
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
use yew::{prelude::*, web_sys};

#[derive(Serialize)]
struct FormData {
Expand All @@ -18,6 +21,7 @@ pub struct Form {
fetch_task: Option<FetchTask>,
loading: bool,
validation_errors: ValidationErrors,
success_message: Option<String>,
}

#[derive(Default)]
Expand All @@ -33,6 +37,7 @@ pub enum Msg {
UpdateContent(String),
Submit,
Response(Result<(), anyhow::Error>),
CloseSuccessMessage,
}

impl Component for Form {
Expand All @@ -48,6 +53,7 @@ impl Component for Form {
fetch_task: None,
loading: false,
validation_errors: ValidationErrors::default(),
success_message: None,
}
}

Expand Down Expand Up @@ -97,24 +103,46 @@ impl Component for Form {
FetchService::fetch(request, callback).expect("Failed to start request");
self.fetch_task = Some(task);
}

true
}
Msg::Response(result) => {
self.loading = false;
self.fetch_task = None;
match result {
Ok(_) => {
log::info!("Request successful");
self.name = String::new();
self.email = String::new();
self.content = String::new();
self.validation_errors = ValidationErrors::default();
self.success_message = Some("문의가 성공적으로 접수되었습니다.".into());

if let Some(window) = window() {
let link = self.link.clone();
let closure = Closure::wrap(Box::new(move || {
link.send_message(Msg::CloseSuccessMessage);
})
as Box<dyn Fn()>);

window
.set_timeout_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(),
3100,
)
.unwrap();

closure.forget();
}
}
Err(err) => {
log::error!("Request failed: {:?}", err);
}
Err(err) => log::error!("Request failed: {:?}", err),
}
true
}
Msg::CloseSuccessMessage => {
self.success_message = None;
true
}
}
}

Expand All @@ -126,74 +154,88 @@ impl Component for Form {
let is_disabled = self.loading || !self.is_valid();

html! {
<form onsubmit=self.link.callback(|e: FocusEvent| {
e.prevent_default();
Msg::Submit
})>
<div>
<label for="name">{"이름:"}</label>
<input
type="text"
id="name"
value={self.name.clone()}
placeholder="이름을 작성해주세요."
oninput=self.link.callback(|e: InputData| Msg::UpdateName(e.value))
/>
{
if let Some(error) = &self.validation_errors.name {
html! { <p style="color: red;">{error}</p> }
} else {
html! {}
<div>
{
if self.success_message.is_some() {
html! {
<div class="success-message show">
<p>{ self.success_message.as_ref().unwrap() }</p>
</div>
}
} else {
html! {}
}
</div>
<div>
<label for="email">{"이메일:"}</label>
<input
type="email"
id="email"
placeholder="답변을 받을 이메일을 작성해주세요."
value=self.email.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdateEmail(e.value))
/>
{
if let Some(error) = &self.validation_errors.email {
html! { <p style="color: red;">{error}</p> }
} else {
html! {}
}

<form onsubmit=self.link.callback(|e: FocusEvent| {
e.prevent_default();
Msg::Submit
})>
<div>
<label for="name">{"이름:"}</label>
<input
type="text"
id="name"
value={self.name.clone()}
placeholder="이름을 작성해주세요."
oninput=self.link.callback(|e: InputData| Msg::UpdateName(e.value))
/>
{
if let Some(error) = &self.validation_errors.name {
html! { <p class="error">{error}</p> }
} else {
html! {}
}
}
}
</div>
<div>
<label for="content">{"내용:"}</label>
<textarea
id="content"
placeholder="내용을 작성해주세요."
value={self.content.clone()}
oninput=self.link.callback(|e: InputData| Msg::UpdateContent(e.value))
/>
{
if let Some(error) = &self.validation_errors.content {
html! { <p style="color: red;">{error}</p> }
} else {
html! {}
</div>
<div>
<label for="email">{"이메일:"}</label>
<input
type="email"
id="email"
placeholder="답변을 받을 이메일을 작성해주세요."
value={self.email.clone()}
oninput=self.link.callback(|e: InputData| Msg::UpdateEmail(e.value))
/>
{
if let Some(error) = &self.validation_errors.email {
html! { <p class="error">{error}</p> }
} else {
html! {}
}
}
}
</div>
<button type="submit" disabled=is_disabled>
{
if self.loading {
html! {
<>
<span class="spinner"></span>
</>
</div>
<div>
<label for="content">{"내용:"}</label>
<textarea
id="content"
placeholder="내용을 작성해주세요."
value={self.content.clone()}
oninput=self.link.callback(|e: InputData| Msg::UpdateContent(e.value))
/>
{
if let Some(error) = &self.validation_errors.content {
html! { <p class="error">{error}</p> }
} else {
html! {}
}
} else {
{ html! { "문의하기" } }
}
}
</button>
</form>
</div>
<button type="submit" disabled=is_disabled>
{
if self.loading {
html! {
<>
<span class="spinner"></span>
</>
}
} else {
{ html! { "문의하기" } }
}
}
</button>
</form>
</div>
}
}
}
Expand All @@ -211,7 +253,7 @@ impl Form {
if self.email.trim().is_empty() {
self.validation_errors.email = Some("이메일을 작성해주세요.".into());
} else if !self.email.contains("@") {
self.validation_errors.email = Some("올바른 이메일 주소를 입력해주세요.".into());
self.validation_errors.email = Some("올바른 이메일 주소를 작성해주세요.".into());
} else {
self.validation_errors.email = None;
}
Expand Down
38 changes: 38 additions & 0 deletions frontend/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,42 @@ button[type="submit"]:active {
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

.success-message {
opacity: 0;
animation: fadeInOut 3s ease-in-out forwards;
background-color: #d4edda;
color: #155724;
padding: 16px;
margin-bottom: 16px;
border: 1px solid #c3e6cb;
border-radius: 4px;
margin-left: 40px;
margin-right: 40px;
}

@keyframes fadeInOut {
0% {
opacity: 0;
transform: translateY(-20px);
}
10% {
opacity: 1;
transform: translateY(0);
}
90% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(-20px);
}
}

.error {
color: red;
font-size: 12px;
margin-top: 4px;
}

0 comments on commit 05671f5

Please sign in to comment.