This commit is contained in:
Generated
+2
-1
@@ -3255,9 +3255,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-petting"
|
name = "web-petting"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"chrono-tz",
|
||||||
"cot",
|
"cot",
|
||||||
"futures",
|
"futures",
|
||||||
"multer",
|
"multer",
|
||||||
|
|||||||
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "web-petting"
|
name = "web-petting"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cot = { version = "0.6.0", features = ["sqlite"] }
|
cot = { version = "0.6.0", features = ["sqlite"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
chrono-tz = "0.10"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_html_form = "0.4"
|
serde_html_form = "0.4"
|
||||||
password-auth = "1"
|
password-auth = "1"
|
||||||
|
|||||||
+39
-31
@@ -109,7 +109,7 @@ fn rand_client_color() -> &'static str {
|
|||||||
CLIENT_COLORS[(h.finish() as usize) % CLIENT_COLORS.len()]
|
CLIENT_COLORS[(h.finish() as usize) % CLIENT_COLORS.len()]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now() -> chrono::NaiveDateTime {
|
fn now_utc() -> chrono::NaiveDateTime {
|
||||||
chrono::Utc::now().naive_utc()
|
chrono::Utc::now().naive_utc()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +260,7 @@ struct ScheduleTemplate<'a> {
|
|||||||
t: &'a Translations,
|
t: &'a Translations,
|
||||||
lang: Lang,
|
lang: Lang,
|
||||||
admin_name: &'a str,
|
admin_name: &'a str,
|
||||||
|
timezone: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Template)]
|
#[derive(Debug, Template)]
|
||||||
@@ -399,8 +400,8 @@ async fn setup_submit(request: Request, session: Session, db: Database) -> cot::
|
|||||||
password_hash: password_auth::generate_hash(&form.password),
|
password_hash: password_auth::generate_hash(&form.password),
|
||||||
display_name: display,
|
display_name: display,
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
user.save(&db).await?;
|
user.save(&db).await?;
|
||||||
|
|
||||||
@@ -468,7 +469,8 @@ async fn admin_index(request: Request, session: Session, db: Database) -> cot::R
|
|||||||
Err(resp) => return Ok(resp),
|
Err(resp) => return Ok(resp),
|
||||||
};
|
};
|
||||||
let user_id = get_admin_id(&session).await.unwrap_or(0);
|
let user_id = get_admin_id(&session).await.unwrap_or(0);
|
||||||
let today = chrono::Utc::now().date_naive();
|
let tz = crate::tz::load_tz(&db).await;
|
||||||
|
let today = crate::tz::today_in_tz(tz);
|
||||||
|
|
||||||
let all_visits = Visit::objects().all(&db).await?;
|
let all_visits = Visit::objects().all(&db).await?;
|
||||||
let clients = Client::objects().all(&db).await?;
|
let clients = Client::objects().all(&db).await?;
|
||||||
@@ -664,7 +666,7 @@ async fn client_edit_submit(
|
|||||||
if let Some(color) = form.color.filter(|s| !s.trim().is_empty()) {
|
if let Some(color) = form.color.filter(|s| !s.trim().is_empty()) {
|
||||||
client.color = Some(color);
|
client.color = Some(color);
|
||||||
}
|
}
|
||||||
client.updated_at = now();
|
client.updated_at = now_utc();
|
||||||
client.save(&db).await?;
|
client.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
||||||
@@ -740,8 +742,8 @@ async fn add_user(request: Request, session: Session, db: Database) -> cot::Resu
|
|||||||
password_hash: password_auth::generate_hash(&form.password),
|
password_hash: password_auth::generate_hash(&form.password),
|
||||||
display_name: display,
|
display_name: display,
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
user.save(&db).await?;
|
user.save(&db).await?;
|
||||||
None
|
None
|
||||||
@@ -770,6 +772,7 @@ struct SettingsForm {
|
|||||||
telegram_chat_id: String,
|
telegram_chat_id: String,
|
||||||
contact_info: String,
|
contact_info: String,
|
||||||
pricing_info: String,
|
pricing_info: String,
|
||||||
|
timezone: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_settings(request: Request, session: Session, db: Database) -> cot::Result<Response> {
|
async fn save_settings(request: Request, session: Session, db: Database) -> cot::Result<Response> {
|
||||||
@@ -784,13 +787,14 @@ async fn save_settings(request: Request, session: Session, db: Database) -> cot:
|
|||||||
("telegram_chat_id", form.telegram_chat_id),
|
("telegram_chat_id", form.telegram_chat_id),
|
||||||
("contact_info", form.contact_info),
|
("contact_info", form.contact_info),
|
||||||
("pricing_info", form.pricing_info),
|
("pricing_info", form.pricing_info),
|
||||||
|
("timezone", form.timezone),
|
||||||
] {
|
] {
|
||||||
let k = key.to_string();
|
let k = key.to_string();
|
||||||
let existing = query!(Setting, $key == k).get(&db).await?;
|
let existing = query!(Setting, $key == k).get(&db).await?;
|
||||||
match existing {
|
match existing {
|
||||||
Some(mut s) => {
|
Some(mut s) => {
|
||||||
s.value = value;
|
s.value = value;
|
||||||
s.updated_at = now();
|
s.updated_at = now_utc();
|
||||||
s.save(&db).await?;
|
s.save(&db).await?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -798,7 +802,7 @@ async fn save_settings(request: Request, session: Session, db: Database) -> cot:
|
|||||||
id: Auto::auto(),
|
id: Auto::auto(),
|
||||||
key: key.to_string(),
|
key: key.to_string(),
|
||||||
value,
|
value,
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
s.save(&db).await?;
|
s.save(&db).await?;
|
||||||
}
|
}
|
||||||
@@ -835,7 +839,7 @@ async fn lead_set_status(
|
|||||||
|
|
||||||
if let Some(mut lead) = query!(Lead, $id == lead_id).get(&db).await? {
|
if let Some(mut lead) = query!(Lead, $id == lead_id).get(&db).await? {
|
||||||
lead.status = form.status;
|
lead.status = form.status;
|
||||||
lead.updated_at = now();
|
lead.updated_at = now_utc();
|
||||||
lead.save(&db).await?;
|
lead.save(&db).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,14 +868,14 @@ async fn lead_convert(
|
|||||||
media_token: rand_token(),
|
media_token: rand_token(),
|
||||||
color: Some(rand_client_color().to_string()),
|
color: Some(rand_client_color().to_string()),
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
client.save(&db).await?;
|
client.save(&db).await?;
|
||||||
|
|
||||||
lead.status = "converted".to_string();
|
lead.status = "converted".to_string();
|
||||||
lead.client_id = Some(ForeignKey::from(&client));
|
lead.client_id = Some(ForeignKey::from(&client));
|
||||||
lead.updated_at = now();
|
lead.updated_at = now_utc();
|
||||||
lead.save(&db).await?;
|
lead.save(&db).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,7 +894,7 @@ async fn client_archive(
|
|||||||
}
|
}
|
||||||
if let Some(mut client) = query!(Client, $id == client_id).get(&db).await? {
|
if let Some(mut client) = query!(Client, $id == client_id).get(&db).await? {
|
||||||
client.status = "archived".to_string();
|
client.status = "archived".to_string();
|
||||||
client.updated_at = now();
|
client.updated_at = now_utc();
|
||||||
client.save(&db).await?;
|
client.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
||||||
@@ -908,7 +912,7 @@ async fn client_activate(
|
|||||||
}
|
}
|
||||||
if let Some(mut client) = query!(Client, $id == client_id).get(&db).await? {
|
if let Some(mut client) = query!(Client, $id == client_id).get(&db).await? {
|
||||||
client.status = "active".to_string();
|
client.status = "active".to_string();
|
||||||
client.updated_at = now();
|
client.updated_at = now_utc();
|
||||||
client.save(&db).await?;
|
client.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/clients?lang={}", lang.code())).into_response()
|
||||||
@@ -926,7 +930,7 @@ async fn user_archive(
|
|||||||
}
|
}
|
||||||
if let Some(mut user) = query!(User, $id == user_id).get(&db).await? {
|
if let Some(mut user) = query!(User, $id == user_id).get(&db).await? {
|
||||||
user.status = "archived".to_string();
|
user.status = "archived".to_string();
|
||||||
user.updated_at = now();
|
user.updated_at = now_utc();
|
||||||
user.save(&db).await?;
|
user.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/users?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/users?lang={}", lang.code())).into_response()
|
||||||
@@ -944,7 +948,7 @@ async fn user_activate(
|
|||||||
}
|
}
|
||||||
if let Some(mut user) = query!(User, $id == user_id).get(&db).await? {
|
if let Some(mut user) = query!(User, $id == user_id).get(&db).await? {
|
||||||
user.status = "active".to_string();
|
user.status = "active".to_string();
|
||||||
user.updated_at = now();
|
user.updated_at = now_utc();
|
||||||
user.save(&db).await?;
|
user.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/users?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/users?lang={}", lang.code())).into_response()
|
||||||
@@ -972,8 +976,8 @@ async fn add_lead(request: Request, session: Session, db: Database) -> cot::Resu
|
|||||||
comment: form.comment.filter(|s| !s.trim().is_empty()),
|
comment: form.comment.filter(|s| !s.trim().is_empty()),
|
||||||
status: "new".to_string(),
|
status: "new".to_string(),
|
||||||
client_id: None,
|
client_id: None,
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
lead.save(&db).await?;
|
lead.save(&db).await?;
|
||||||
|
|
||||||
@@ -1014,8 +1018,8 @@ async fn add_client(request: Request, session: Session, db: Database) -> cot::Re
|
|||||||
media_token: rand_token(),
|
media_token: rand_token(),
|
||||||
color: Some(rand_client_color().to_string()),
|
color: Some(rand_client_color().to_string()),
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
client.save(&db).await?;
|
client.save(&db).await?;
|
||||||
|
|
||||||
@@ -1026,16 +1030,18 @@ async fn add_client(request: Request, session: Session, db: Database) -> cot::Re
|
|||||||
// Schedule Handlers
|
// Schedule Handlers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async fn schedule_page(request: Request, session: Session) -> cot::Result<Response> {
|
async fn schedule_page(request: Request, session: Session, db: Database) -> cot::Result<Response> {
|
||||||
let lang = detect_lang(&request);
|
let lang = detect_lang(&request);
|
||||||
let admin_name = match require_auth(&session, lang).await {
|
let admin_name = match require_auth(&session, lang).await {
|
||||||
Ok(name) => name,
|
Ok(name) => name,
|
||||||
Err(resp) => return Ok(resp),
|
Err(resp) => return Ok(resp),
|
||||||
};
|
};
|
||||||
|
let tz = crate::tz::load_tz(&db).await;
|
||||||
let body = ScheduleTemplate {
|
let body = ScheduleTemplate {
|
||||||
t: lang.t(),
|
t: lang.t(),
|
||||||
lang,
|
lang,
|
||||||
admin_name: &admin_name,
|
admin_name: &admin_name,
|
||||||
|
timezone: tz.to_string(),
|
||||||
}
|
}
|
||||||
.render()?;
|
.render()?;
|
||||||
html_response(body, lang)
|
html_response(body, lang)
|
||||||
@@ -1089,10 +1095,12 @@ async fn schedule_events(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tz = crate::tz::load_tz(&db).await;
|
||||||
|
let tz_today = crate::tz::today_in_tz(tz);
|
||||||
let start_date = chrono::NaiveDate::parse_from_str(start_str, "%Y-%m-%d")
|
let start_date = chrono::NaiveDate::parse_from_str(start_str, "%Y-%m-%d")
|
||||||
.unwrap_or_else(|_| chrono::Utc::now().date_naive() - chrono::Duration::days(60));
|
.unwrap_or_else(|_| tz_today - chrono::Duration::days(60));
|
||||||
let end_date = chrono::NaiveDate::parse_from_str(end_str, "%Y-%m-%d")
|
let end_date = chrono::NaiveDate::parse_from_str(end_str, "%Y-%m-%d")
|
||||||
.unwrap_or_else(|_| chrono::Utc::now().date_naive() + chrono::Duration::days(60));
|
.unwrap_or_else(|_| tz_today + chrono::Duration::days(60));
|
||||||
|
|
||||||
let visits = Visit::objects().all(&db).await?;
|
let visits = Visit::objects().all(&db).await?;
|
||||||
let clients = Client::objects().all(&db).await?;
|
let clients = Client::objects().all(&db).await?;
|
||||||
@@ -1203,8 +1211,8 @@ async fn schedule_create(
|
|||||||
public_notes: None,
|
public_notes: None,
|
||||||
client_feedback: None,
|
client_feedback: None,
|
||||||
status: "scheduled".to_string(),
|
status: "scheduled".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
visit.save(&db).await?;
|
visit.save(&db).await?;
|
||||||
}
|
}
|
||||||
@@ -1286,7 +1294,7 @@ async fn schedule_edit_submit(
|
|||||||
visit.status = form.status;
|
visit.status = form.status;
|
||||||
visit.notes = form.notes.filter(|s| !s.trim().is_empty());
|
visit.notes = form.notes.filter(|s| !s.trim().is_empty());
|
||||||
visit.public_notes = form.public_notes.filter(|s| !s.trim().is_empty());
|
visit.public_notes = form.public_notes.filter(|s| !s.trim().is_empty());
|
||||||
visit.updated_at = now();
|
visit.updated_at = now_utc();
|
||||||
visit.save(&db).await?;
|
visit.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
||||||
@@ -1318,7 +1326,7 @@ async fn visit_set_done(
|
|||||||
}
|
}
|
||||||
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
||||||
visit.status = "completed".to_string();
|
visit.status = "completed".to_string();
|
||||||
visit.updated_at = now();
|
visit.updated_at = now_utc();
|
||||||
visit.save(&db).await?;
|
visit.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
||||||
@@ -1336,7 +1344,7 @@ async fn visit_set_cancel(
|
|||||||
}
|
}
|
||||||
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
||||||
visit.status = "cancelled".to_string();
|
visit.status = "cancelled".to_string();
|
||||||
visit.updated_at = now();
|
visit.updated_at = now_utc();
|
||||||
visit.save(&db).await?;
|
visit.save(&db).await?;
|
||||||
}
|
}
|
||||||
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
Redirect::new(format!("/admin/schedule?lang={}", lang.code())).into_response()
|
||||||
@@ -1556,7 +1564,7 @@ async fn media_upload_submit(
|
|||||||
file_type: ftype,
|
file_type: ftype,
|
||||||
caption: caption_opt.clone(),
|
caption: caption_opt.clone(),
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
};
|
};
|
||||||
media.save(&db).await?;
|
media.save(&db).await?;
|
||||||
}
|
}
|
||||||
@@ -1759,7 +1767,7 @@ async fn testimonial_add(
|
|||||||
image_path,
|
image_path,
|
||||||
status: "active".to_string(),
|
status: "active".to_string(),
|
||||||
sort_order: max_order + 1,
|
sort_order: max_order + 1,
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
};
|
};
|
||||||
testimonial.save(&db).await?;
|
testimonial.save(&db).await?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ pub struct Translations {
|
|||||||
pub settings_telegram_chat_id: &'static str,
|
pub settings_telegram_chat_id: &'static str,
|
||||||
pub settings_contact_info: &'static str,
|
pub settings_contact_info: &'static str,
|
||||||
pub settings_pricing_info: &'static str,
|
pub settings_pricing_info: &'static str,
|
||||||
|
pub settings_timezone: &'static str,
|
||||||
pub landing_contact_label: &'static str,
|
pub landing_contact_label: &'static str,
|
||||||
pub landing_pricing_title: &'static str,
|
pub landing_pricing_title: &'static str,
|
||||||
|
|
||||||
@@ -338,6 +339,7 @@ static RU: Translations = Translations {
|
|||||||
settings_telegram_chat_id: "Chat ID для уведомлений",
|
settings_telegram_chat_id: "Chat ID для уведомлений",
|
||||||
settings_contact_info: "Контактная информация (отображается на лендинге)",
|
settings_contact_info: "Контактная информация (отображается на лендинге)",
|
||||||
settings_pricing_info: "Блок с ценами (отображается на лендинге)",
|
settings_pricing_info: "Блок с ценами (отображается на лендинге)",
|
||||||
|
settings_timezone: "Часовой пояс (например Asia/Vladivostok)",
|
||||||
landing_contact_label: "Или свяжитесь с нами напрямую",
|
landing_contact_label: "Или свяжитесь с нами напрямую",
|
||||||
landing_pricing_title: "Стоимость",
|
landing_pricing_title: "Стоимость",
|
||||||
|
|
||||||
@@ -536,6 +538,7 @@ static EN: Translations = Translations {
|
|||||||
settings_telegram_chat_id: "Notification Chat ID",
|
settings_telegram_chat_id: "Notification Chat ID",
|
||||||
settings_contact_info: "Contact info (shown on landing page)",
|
settings_contact_info: "Contact info (shown on landing page)",
|
||||||
settings_pricing_info: "Pricing block (shown on landing page)",
|
settings_pricing_info: "Pricing block (shown on landing page)",
|
||||||
|
settings_timezone: "Timezone (e.g. Asia/Vladivostok)",
|
||||||
landing_contact_label: "Or contact us directly",
|
landing_contact_label: "Or contact us directly",
|
||||||
landing_pricing_title: "Pricing",
|
landing_pricing_title: "Pricing",
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ mod migrations;
|
|||||||
pub mod models;
|
pub mod models;
|
||||||
mod public;
|
mod public;
|
||||||
mod telegram;
|
mod telegram;
|
||||||
|
mod tz;
|
||||||
|
|
||||||
use cot::cli::CliMetadata;
|
use cot::cli::CliMetadata;
|
||||||
use cot::config::{
|
use cot::config::{
|
||||||
|
|||||||
+6
-5
@@ -60,7 +60,7 @@ fn html_response(body: String, lang: Lang) -> cot::Result<Response> {
|
|||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now() -> chrono::NaiveDateTime {
|
fn now_utc() -> chrono::NaiveDateTime {
|
||||||
chrono::Utc::now().naive_utc()
|
chrono::Utc::now().naive_utc()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,8 +131,8 @@ async fn submit_lead(request: Request, db: Database) -> cot::Result<Response> {
|
|||||||
comment: form.comment.filter(|s| !s.trim().is_empty()),
|
comment: form.comment.filter(|s| !s.trim().is_empty()),
|
||||||
status: "new".to_string(),
|
status: "new".to_string(),
|
||||||
client_id: None,
|
client_id: None,
|
||||||
created_at: now(),
|
created_at: now_utc(),
|
||||||
updated_at: now(),
|
updated_at: now_utc(),
|
||||||
};
|
};
|
||||||
lead.save(&db).await?;
|
lead.save(&db).await?;
|
||||||
|
|
||||||
@@ -188,7 +188,8 @@ async fn client_portal(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client_id = client.id.unwrap();
|
let client_id = client.id.unwrap();
|
||||||
let today = chrono::Utc::now().date_naive();
|
let tz = crate::tz::load_tz(&db).await;
|
||||||
|
let today = crate::tz::today_in_tz(tz);
|
||||||
|
|
||||||
let mut visits = Visit::objects().all(&db).await?;
|
let mut visits = Visit::objects().all(&db).await?;
|
||||||
visits.retain(|v| v.client_id.primary_key().unwrap() == client_id && v.status != "cancelled");
|
visits.retain(|v| v.client_id.primary_key().unwrap() == client_id && v.status != "cancelled");
|
||||||
@@ -277,7 +278,7 @@ async fn submit_feedback(
|
|||||||
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
if let Some(mut visit) = query!(Visit, $id == visit_id).get(&db).await? {
|
||||||
if visit.client_id.primary_key().unwrap() == client_id {
|
if visit.client_id.primary_key().unwrap() == client_id {
|
||||||
visit.client_feedback = Some(form.feedback);
|
visit.client_feedback = Some(form.feedback);
|
||||||
visit.updated_at = now();
|
visit.updated_at = now_utc();
|
||||||
visit.save(&db).await?;
|
visit.save(&db).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
const calendar = new FullCalendar.Calendar(calEl, {
|
const calendar = new FullCalendar.Calendar(calEl, {
|
||||||
locale: lang,
|
locale: lang,
|
||||||
|
timeZone: '{{ timezone }}',
|
||||||
initialView: window.innerWidth < 768 ? 'listWeek' : 'dayGridMonth',
|
initialView: window.innerWidth < 768 ? 'listWeek' : 'dayGridMonth',
|
||||||
headerToolbar: {
|
headerToolbar: {
|
||||||
left: 'prev,next today',
|
left: 'prev,next today',
|
||||||
|
|||||||
@@ -38,6 +38,12 @@
|
|||||||
<textarea class="input" name="pricing_info" rows="3" style="min-height:70px;resize:vertical;" placeholder="от 600 рублей за визит">{% for s in &settings %}{% if s.key == "pricing_info" %}{{ s.value }}{% endif %}{% endfor %}</textarea>
|
<textarea class="input" name="pricing_info" rows="3" style="min-height:70px;resize:vertical;" placeholder="от 600 рублей за визит">{% for s in &settings %}{% if s.key == "pricing_info" %}{{ s.value }}{% endif %}{% endfor %}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ t.settings_timezone }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" name="timezone" placeholder="Asia/Vladivostok" value="{% for s in &settings %}{% if s.key == "timezone" %}{{ s.value }}{% endif %}{% endfor %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button type="submit" class="button is-primary">{{ t.settings_save }}</button>
|
<button type="submit" class="button is-primary">{{ t.settings_save }}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user