Implemented mail settings.
parent
88ac17008c
commit
d18ef72d03
@ -0,0 +1,81 @@
|
|||||||
|
use cfg_if::cfg_if;
|
||||||
|
use leptos::*;
|
||||||
|
use validator::Validate;
|
||||||
|
use crate::backend::data::{ApiResponse, Message, MessageType};
|
||||||
|
use crate::components::data_form::ForValidation;
|
||||||
|
|
||||||
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||||
|
use sqlx::{PgPool, query, query_as};
|
||||||
|
use sqlx::Error;
|
||||||
|
use crate::backend::get_pool;
|
||||||
|
use crate::error::AppError;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
pub async fn message_for_type(msg_type: &MessageType, pool: &PgPool) -> Result<Message, Error> {
|
||||||
|
Ok(query_as::<_, Message>("SELECT * FROM message WHERE msg_type = $1")
|
||||||
|
.bind(msg_type)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_message(msg_type: &MessageType, pool: &PgPool) -> Result<(), Error> {
|
||||||
|
query("INSERT INTO message(msg_type, subject, text) VALUES($1, '', '')")
|
||||||
|
.bind(msg_type)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_messages(pool: &PgPool) -> Result<(), AppError> {
|
||||||
|
let types = [
|
||||||
|
MessageType::NewReservation,
|
||||||
|
MessageType::NewReservationCust,
|
||||||
|
MessageType::ReservationApp,
|
||||||
|
MessageType::ReservationCanceled];
|
||||||
|
|
||||||
|
for msg_type in types {
|
||||||
|
let msg = message_for_type(&msg_type, pool).await;
|
||||||
|
if let Err(e) = msg {
|
||||||
|
if matches!(e, Error::RowNotFound) {
|
||||||
|
info!("Creating initial message for type {:?}", msg_type);
|
||||||
|
init_message(&msg_type, pool).await?;
|
||||||
|
} else {
|
||||||
|
return Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn get_message(msg_type: MessageType) -> Result<Message, ServerFnError> {
|
||||||
|
let pool = get_pool().await?;
|
||||||
|
|
||||||
|
Ok(message_for_type(&msg_type, &pool).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn update_message(message: Message) -> Result<ApiResponse<()>, ServerFnError> {
|
||||||
|
use crate::perm_check;
|
||||||
|
|
||||||
|
perm_check!(is_admin);
|
||||||
|
let pool = get_pool().await?;
|
||||||
|
|
||||||
|
query("UPDATE message SET subject = $1, text = $2 WHERE msg_type = $3")
|
||||||
|
.bind(message.subject)
|
||||||
|
.bind(message.text)
|
||||||
|
.bind(message.msg_type)
|
||||||
|
.execute(&pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(ApiResponse::Data(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForValidation for UpdateMessage {
|
||||||
|
fn entity(&self) -> &dyn Validate {
|
||||||
|
&self.message
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use crate::backend::data::MessageType;
|
||||||
|
use crate::locales::trl;
|
||||||
|
use crate::pages::mail_view::MailView;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn mail_settings() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<h1>{trl("Mail settings")}</h1>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col-md">
|
||||||
|
<MailView title="New booking" mail_type=MessageType::NewReservation/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md">
|
||||||
|
<MailView title="New booking - for customer" mail_type=MessageType::NewReservationCust/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-5">
|
||||||
|
<div class="col-md">
|
||||||
|
<MailView title="Booking approved" mail_type=MessageType::ReservationApp/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md">
|
||||||
|
<MailView title="Booking canceled" mail_type=MessageType::ReservationCanceled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
use leptos::*;
|
||||||
|
use crate::backend::data::{Message, MessageType};
|
||||||
|
use crate::backend::mail::{get_message, UpdateMessage};
|
||||||
|
use crate::components::data_form::DataForm;
|
||||||
|
use crate::components::modal_box::DialogOpener;
|
||||||
|
use crate::locales::trl;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn mail_edit(opener: DialogOpener, mail: ReadSignal<Message>) -> impl IntoView {
|
||||||
|
let update = create_server_action::<UpdateMessage>();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<DataForm opener=opener title="Edit mail" action=update>
|
||||||
|
<input type="hidden" prop:value={move || mail.get().id()} name="message[id]"/>
|
||||||
|
<input type="hidden" prop:value={move || mail.get().msg_type.to_string()} name="message[msg_type]"/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col mb-3">
|
||||||
|
<label for="subject" class="form-label">"Subject"</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="nameWithTitle"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter Subject"
|
||||||
|
prop:value={move || mail.get().subject}
|
||||||
|
name="message[subject]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col mb-3">
|
||||||
|
<label for="text" class="form-label">"Text"</label>
|
||||||
|
<textarea
|
||||||
|
id="text"
|
||||||
|
class="form-control"
|
||||||
|
prop:value={move || mail.get().text}
|
||||||
|
name="message[text]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DataForm>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn mail_view(title: &'static str, mail_type: MessageType) -> impl IntoView {
|
||||||
|
let editor = DialogOpener::new();
|
||||||
|
let mail = create_blocking_resource( move || editor.visible(), move |_| get_message(mail_type.clone()));
|
||||||
|
let for_edit = create_rw_signal(Message::default());
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<MailEdit opener=editor mail={for_edit.read_only()}/>
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title"><i class="bx bx-buildings"></i>" "{trl(title)}</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<Transition fallback=move || view! {<p>{trl("Loading...")}</p> }>
|
||||||
|
{
|
||||||
|
mail.get().map(|m| match m {
|
||||||
|
Ok(m) => {
|
||||||
|
for_edit.set(m.clone());
|
||||||
|
view! {
|
||||||
|
<div>
|
||||||
|
<p>{trl("Subject: ")}{m.subject}</p>
|
||||||
|
<p>{m.text}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
view! {
|
||||||
|
<div>"Error loading data"</div>
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
</Transition>
|
||||||
|
</p>
|
||||||
|
<a href="javascript:void(0)" class="card-link" on:click = move |_| editor.show()>
|
||||||
|
<i class="bx bx-edit-alt fs-4 lh-0"></i></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue