Allow celendar event editing
This commit is contained in:
parent
fe8056179b
commit
d156cbfa98
|
|
@ -4,15 +4,18 @@ use axum::{
|
|||
response::{Html, IntoResponse, Redirect, Response},
|
||||
Extension, Form,
|
||||
};
|
||||
use chrono::Days;
|
||||
use chrono::{Days, TimeDelta};
|
||||
use http::StatusCode;
|
||||
use rbac::RbacService;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use sqlx::{postgres::PgRow, Error, FromRow, PgPool, Row};
|
||||
use uuid::Uuid;
|
||||
use rbac::RbacService;
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized, rbac, user::{get_user_roles_display, AccountData}
|
||||
middlewares::is_authorized,
|
||||
rbac,
|
||||
user::{get_user_roles_display, AccountData},
|
||||
};
|
||||
|
||||
struct HtmlTemplate<T>(T);
|
||||
|
|
@ -162,6 +165,10 @@ pub async fn get_events(
|
|||
// Extract the user data.
|
||||
let _user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
let personid = user_data
|
||||
.as_ref()
|
||||
.map(|s| s.person_id.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
// Set empty query string
|
||||
let mut query = r#""#;
|
||||
|
|
@ -172,6 +179,7 @@ pub async fn get_events(
|
|||
} else {
|
||||
query = r#"select to_json(json_agg(jbo.val))
|
||||
from (select json_build_object(
|
||||
'id', ce.id,
|
||||
'title', ce.title,
|
||||
'start', ce.start_time,
|
||||
'end', ce.end_time,
|
||||
|
|
@ -187,6 +195,7 @@ pub async fn get_events(
|
|||
and ce.created_by = $3
|
||||
union all
|
||||
select json_build_object(
|
||||
'id', ce.id,
|
||||
'title', 'In use',
|
||||
'start', ce.start_time,
|
||||
'end', ce.end_time,
|
||||
|
|
@ -203,6 +212,7 @@ pub async fn get_events(
|
|||
}
|
||||
} else {
|
||||
query = r#"select to_json(json_agg(json_build_object(
|
||||
'id', ce.id,
|
||||
'title', ce.title,
|
||||
'start', ce.start_time,
|
||||
'end', ce.end_time,
|
||||
|
|
@ -218,6 +228,7 @@ pub async fn get_events(
|
|||
}
|
||||
} else {
|
||||
query = r#"select to_json(json_agg(json_build_object(
|
||||
'id', ce.id,
|
||||
'title', ce.title,
|
||||
'start', ce.start_time,
|
||||
'end', ce.end_time,
|
||||
|
|
@ -237,10 +248,10 @@ pub async fn get_events(
|
|||
//println!("User is authorized");
|
||||
|
||||
// Get requested calendar events from database
|
||||
let events = sqlx::query(query,)
|
||||
let events = sqlx::query(query)
|
||||
.bind(chrono::DateTime::parse_from_rfc3339(¶ms.start).unwrap())
|
||||
.bind(chrono::DateTime::parse_from_rfc3339(¶ms.end).unwrap())
|
||||
.bind(userid)
|
||||
.bind(personid)
|
||||
.fetch_one(&db_pool)
|
||||
.await;
|
||||
|
||||
|
|
@ -297,7 +308,10 @@ pub async fn create_event(
|
|||
if is_authorized("/calendar", user_data.clone(), db_pool.clone()).await {
|
||||
let fmt = "%Y-%m-%d";
|
||||
let start_date = chrono::NaiveDate::parse_from_str(&event.start_time, fmt).unwrap();
|
||||
let end_date = chrono::NaiveDate::parse_from_str(&event.end_time, fmt).unwrap().checked_sub_days(Days::new(1)).unwrap();
|
||||
let end_date = chrono::NaiveDate::parse_from_str(&event.end_time, fmt)
|
||||
.unwrap()
|
||||
.checked_sub_days(Days::new(1))
|
||||
.unwrap();
|
||||
let start_datetime = start_date.and_hms_opt(14, 0, 0).unwrap();
|
||||
let end_datetime = end_date.and_hms_opt(10, 0, 0).unwrap();
|
||||
|
||||
|
|
@ -378,7 +392,10 @@ pub async fn new_request(
|
|||
|
||||
let fmt = "%Y-%m-%d";
|
||||
let start_date = chrono::NaiveDate::parse_from_str(¶ms.start, fmt).unwrap();
|
||||
let end_date = chrono::NaiveDate::parse_from_str(¶ms.end, fmt).unwrap().checked_sub_days(Days::new(1)).unwrap();
|
||||
let end_date = chrono::NaiveDate::parse_from_str(¶ms.end, fmt)
|
||||
.unwrap()
|
||||
.checked_sub_days(Days::new(1))
|
||||
.unwrap();
|
||||
let start_datetime = start_date.and_hms_opt(14, 0, 0).unwrap();
|
||||
let end_datetime = end_date.and_hms_opt(10, 0, 0).unwrap();
|
||||
|
||||
|
|
@ -423,6 +440,91 @@ pub async fn new_request(
|
|||
eventstring
|
||||
}
|
||||
|
||||
pub async fn update_event(
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<AccountData>>,
|
||||
Extension(rbac): Extension<RbacService>,
|
||||
request: axum::http::Request<axum::body::Body>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
||||
// Set default events
|
||||
let mut eventstring: String = "[]".to_string();
|
||||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
let _user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
let personid = user_data
|
||||
.as_ref()
|
||||
.map(|s| s.person_id.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
if rbac.has_permission(userid, "calendar:*:*").await {
|
||||
let (_parts, body) = request.into_parts();
|
||||
let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap();
|
||||
let body_str = String::from_utf8(bytes.to_vec()).unwrap();
|
||||
// println!("Body: {}", body_str);
|
||||
|
||||
let v: Value = serde_json::from_str(&body_str).unwrap();
|
||||
|
||||
let fmt = "%Y-%m-%d";
|
||||
|
||||
let start_date = chrono::NaiveDate::parse_from_str(v["start"].as_str().unwrap(), fmt).unwrap();
|
||||
let end_date = chrono::NaiveDate::parse_from_str(v["end"].as_str().unwrap(), fmt).unwrap();
|
||||
let start_datetime = start_date.and_hms_opt(14, 0, 0).unwrap();
|
||||
let end_datetime = end_date.and_hms_opt(10, 0, 0).unwrap();
|
||||
|
||||
// Convert calendar id to UUID
|
||||
let calendar_event_id = Uuid::parse_str(v["id"].as_str().unwrap()).unwrap();
|
||||
|
||||
// Display values to be updated
|
||||
// println!("person : {}", personid);
|
||||
// println!("title : {}", v["title"]);
|
||||
// println!("start : {:#?}", start_datetime);
|
||||
// println!("end : {:#?}", end_datetime);
|
||||
|
||||
let event = sqlx::query_scalar::<_, uuid::Uuid>(
|
||||
r#"with cet as (select id from calendar_event_types where name = 'Reservation' and state = 'Requested')
|
||||
update calendar_events
|
||||
set updated_by = $1,
|
||||
updated_at = now(),
|
||||
event_type_id = cet.id,
|
||||
title = $2,
|
||||
start_time = $3,
|
||||
end_time = $4
|
||||
from cet
|
||||
where calendar_events.id = $5
|
||||
returning calendar_events.id"#
|
||||
)
|
||||
.bind(personid)
|
||||
.bind(v["title"].as_str().unwrap())
|
||||
.bind(start_datetime)
|
||||
.bind(end_datetime)
|
||||
.bind(calendar_event_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error creating event: {}", e),
|
||||
)
|
||||
});
|
||||
|
||||
let event_id = event.clone();
|
||||
|
||||
// println!("Event: {:#?}", event);
|
||||
|
||||
eventstring = get_event(event_id.unwrap(), &db_pool).await;
|
||||
|
||||
// println!("{:#?}", eventstring);
|
||||
}
|
||||
}
|
||||
|
||||
eventstring
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "newevent.html")]
|
||||
struct EventTemplate {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ use wishlist::{
|
|||
user_wishlist_returned_item, user_wishlist_save_item, wishlists,
|
||||
};
|
||||
|
||||
use crate::calendar::update_event;
|
||||
|
||||
//use email::send_emails;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -95,6 +97,7 @@ async fn main() {
|
|||
.route("/calendar/createevent", post(create_event))
|
||||
.route("/calendar/newevent", get(new_event))
|
||||
.route("/calendar/newrequest", post(new_request))
|
||||
.route("/calendar/updaterequest", post(update_event))
|
||||
// Wishlist
|
||||
.route("/wishlists", get(wishlists))
|
||||
.route("/userwishlist/{user_id}", get(user_wishlist))
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ fn permission_matches(pattern: &str, resource: &str) -> bool {
|
|||
|
||||
pattern_segments.iter()
|
||||
.zip(resource_segments.iter())
|
||||
.all(|(p, r)| p == r || *p == "*")
|
||||
.all(|(p, r)| p == r || *p == "*" || *r == "*")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div id="calendar" class="fc"></div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<!-- Create Event Modal -->
|
||||
<div class="modal fade" id="eventDetailsModal" tabindex="-1" role="dialog" aria-labelledby="eventDetailsModalTitle"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
|
|
@ -37,6 +37,30 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Event Modal -->
|
||||
<div class="modal fade" id="eventEditModal" tabindex="-1" role="dialog" aria-labelledby="eventEditModalTitle"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form id="eventForm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit Event</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="text" id="eventEditTitle" class="form-control" placeholder="Event Title">
|
||||
<input type="date" id="eventEditStart" class="form-control" placeholder="Start">
|
||||
<input type="date" id="eventEditEnd" class="form-control" placeholder="End">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="closeEventEdit"
|
||||
data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="saveEvent">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock center %}
|
||||
{% block scripts %}
|
||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.17/index.global.min.js'></script>
|
||||
|
|
@ -63,6 +87,16 @@
|
|||
$('#eventStart').val(info.startStr);
|
||||
$('#eventEnd').val(info.endStr);
|
||||
$('#eventDetailsModal').modal('show');
|
||||
},
|
||||
eventClick: function (info) {
|
||||
if (info.event.title != "In use") {
|
||||
$('#eventEditTitle').val(info.event.title);
|
||||
$('#eventEditStart').val(moment(info.event.start).format('YYYY-MM-DD'));
|
||||
$('#eventEditEnd').val(moment(info.event.end).format('YYYY-MM-DD'));
|
||||
$('#eventEditModal').modal('show');
|
||||
// Store the event for later update
|
||||
window.calEvent = info.event;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -110,6 +144,55 @@
|
|||
document.getElementById('eventDetailsModalClose').addEventListener('click', function () {
|
||||
$('#eventDetailsModal').modal('hide');
|
||||
});
|
||||
|
||||
$('#saveEvent').on('click', function () {
|
||||
var updatedEvent = {
|
||||
id: window.calEvent.id,
|
||||
title: $('#eventEditTitle').val(),
|
||||
start: $('#eventEditStart').val(),
|
||||
end: $('#eventEditEnd').val()
|
||||
};
|
||||
|
||||
// Save the updates to the record
|
||||
fetch('/calendar/updaterequest', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updatedEvent)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Optionally, use the response to add the event to the calendar
|
||||
$('#eventDetailsModal').modal('hide');
|
||||
// Update the original event object
|
||||
if (window.calEvent.title != data.title) {
|
||||
window.calEvent.setProp('title', data.title);
|
||||
}
|
||||
if (window.calEvent.start != data.start) {
|
||||
window.calEvent.setStart(data.start);
|
||||
}
|
||||
if (window.calEvent.end != data.end) {
|
||||
window.calEvent.setEnd(data.end);
|
||||
}
|
||||
window.calEvent.setProp('allDay', data.allDay);
|
||||
window.calEvent.setProp('backgroundColor', data.backgroundColor);
|
||||
|
||||
e.target.reset();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error creating event:', error);
|
||||
//alert('An error occurred while creating the event.');
|
||||
});
|
||||
|
||||
// Hide the popup
|
||||
$('#eventEditModal').modal('hide');
|
||||
});
|
||||
|
||||
$('#closeEventEdit').on('click', function () {
|
||||
$('#eventEditModal').modal('hide');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock scripts %}
|
||||
Loading…
Reference in New Issue