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