1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use super::order_details::OrderDetails;
use crate::order::{order_model::OrderFull, view::fetch_user_order};
use crate::{
RecordId,
components::{
Alert, AlertSeverity,
loading::{LoadingIndicator, LoadingIndicatorSize},
},
};
use leptos::prelude::*;
use leptos_router::hooks::use_params_map;
#[server]
pub async fn update_order_edit(order: OrderFull) -> Result<(), ServerFnError> {
let user = crate::session::get_user().await?;
// Check if user is admin
if user.is_admin != Some(true) {
return Err(ServerFnError::ServerError("Unauthorized".into()));
}
let db = crate::db::db_init().await?;
// Check if we need to send tracking email (when order is marked as shipped AND done)
let should_send_tracking_email = order.shipped && order.done;
// Update the order
let _: Option<OrderFull> = db
.update(("order", order.id.key().to_string()))
.content(serde_json::json!({
"courier": order.courier,
"tracking_number": order.tracking_number,
"shipped": order.shipped,
"client_received": order.client_received,
"done": order.done,
}))
.await?;
// Send tracking email if conditions are met
if should_send_tracking_email {
if let Err(e) = send_tracking_email(&order).await {
// Log error but don't fail the order update
tracing::error!("Failed to send tracking email: {:?}", e);
}
}
Ok(())
}
#[cfg(feature = "ssr")]
async fn send_tracking_email(order: &OrderFull) -> Result<(), ServerFnError> {
use crate::email::send_email;
use crate::email_templates;
let user_full = if let Some(uf) = &order.user_full {
uf
} else {
return Err(ServerFnError::new("User details missing for order"));
};
// Get customer email from user_full (already EmailAddress type)
let email_addr = user_full.email.clone();
// Get customer name (name is String, not Option<String>)
let client_name = user_full.name.clone();
// Get order reference
let order_ref = order.order_ref();
// Prepare email
let subject = "Your Scratch Fix Pro Order Has Been Shipped!!!";
let body = email_templates::order_tracking_email(
&client_name,
&order_ref,
&order.courier,
&order.tracking_number,
);
// Send email
match send_email(email_addr, subject, &body).await {
Ok(_) => {
tracing::info!("Tracking email sent successfully for order {}", order_ref);
Ok(())
}
Err(e) => Err(ServerFnError::new(format!("Email send failed: {:?}", e))),
}
}
#[component]
pub fn OrderEdit() -> impl IntoView {
let params = use_params_map();
let order_id = move || params.read().get("order_id").unwrap_or_default();
let order_resource = Resource::new(
move || {
let ordid = order_id();
RecordId::from_table_key("order", &ordid)
},
fetch_user_order,
);
let save_action = ServerAction::<UpdateOrderEdit>::new();
let on_save = Callback::new(move |order: OrderFull| {
save_action.dispatch(UpdateOrderEdit { order });
});
view! {
<Suspense fallback=move || {
view! {
<div class="min-h-screen w-full flex items-center justify-center bg-white dark:bg-neutral-950">
<LoadingIndicator size=LoadingIndicatorSize::Huge />
</div>
}
}>
{move || {
order_resource
.get()
.map(|order_result| {
match order_result {
Ok(order) => {
view! {
<>
{save_action
.value()
.get()
.and_then(|result: Result<(), ServerFnError>| {
match result {
Ok(_) => {
Some(
view! {
<div class="fixed top-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow-lg">
"Order saved successfully!"
</div>
}
.into_any(),
)
}
Err(e) => {
Some(
view! {
<div class="fixed top-4 right-4 bg-red-500 text-white px-4 py-2 rounded shadow-lg">
"Error saving order: " {e.to_string()}
</div>
}
.into_any(),
)
}
}
})}
<OrderDetails order=order is_admin=true on_save=on_save />
</>
}
.into_any()
}
Err(err) => {
view! {
<Alert severity=AlertSeverity::Error>
"Error loading order: " {err.to_string()}
</Alert>
}
.into_any()
}
}
})
}}
</Suspense>
}
}