mirror of
https://github.com/OMGeeky/twba.control-center.git
synced 2025-12-26 17:02:38 +01:00
add editing
This commit is contained in:
11
src/main.rs
11
src/main.rs
@@ -1,11 +1,11 @@
|
||||
use derive_more::{FromStr, FromStrError};
|
||||
use rocket::State;
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use derive_more::FromStr;
|
||||
use derive_more::FromStrError;
|
||||
use rocket::request::FromParam;
|
||||
use rocket::response::Responder;
|
||||
use rocket::tokio::time::{sleep, Duration};
|
||||
use rocket::State;
|
||||
use rocket_dyn_templates::Template;
|
||||
use std::sync::OnceLock;
|
||||
use twba_common::init_tracing;
|
||||
@@ -51,8 +51,9 @@ async fn main() -> Result<(), MainError> {
|
||||
.mount(
|
||||
"/services/",
|
||||
routes![
|
||||
services::service,
|
||||
services::service_info,
|
||||
services::service_index,
|
||||
services::task_edit,
|
||||
services::service_edit,
|
||||
services::update_progress,
|
||||
services::increment_progress,
|
||||
services::increment_task_progress,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use crate::DatabaseConnection;
|
||||
use crate::{AvailableServices, ResponderError};
|
||||
use crate::ResponderError;
|
||||
use rocket::form::Form;
|
||||
use rocket::State;
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use twba_common::prelude::twba_local_db::prelude::{Services, Tasks};
|
||||
use twba_common::prelude::twba_local_db::prelude::Services;
|
||||
use twba_common::prelude::twba_local_db::prelude::ServicesModel;
|
||||
use twba_common::prelude::twba_local_db::prelude::Tasks;
|
||||
use twba_common::prelude::twba_local_db::prelude::TasksModel;
|
||||
use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveModelTrait;
|
||||
use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveValue;
|
||||
use twba_common::prelude::twba_local_db::re_exports::sea_orm::{EntityTrait, IntoActiveModel};
|
||||
use twba_common::prelude::twba_local_db::re_exports::sea_orm::EntityTrait;
|
||||
use twba_common::prelude::twba_local_db::re_exports::sea_orm::IntoActiveModel;
|
||||
|
||||
async fn get_services(db: &DatabaseConnection) -> Result<Vec<Service>, ResponderError> {
|
||||
let mut list = vec![];
|
||||
@@ -41,6 +46,11 @@ pub struct Service {
|
||||
last_update: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, FromForm)]
|
||||
pub struct IdValueForm<T> {
|
||||
id: i32,
|
||||
value: T,
|
||||
}
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct Task {
|
||||
id: i32,
|
||||
@@ -50,12 +60,37 @@ pub struct Task {
|
||||
max_progress: i32,
|
||||
}
|
||||
|
||||
#[get("/<service>/info")]
|
||||
pub(super) fn service_info(service: AvailableServices) -> String {
|
||||
format!("Here is some info about the service: name: {service}")
|
||||
#[post("/edit-service", data = "<data>")]
|
||||
pub async fn service_edit(
|
||||
db: &State<DatabaseConnection>,
|
||||
data: Form<IdValueForm<String>>,
|
||||
) -> Result<(), ResponderError> {
|
||||
let data = data.into_inner();
|
||||
let model = get_service_by_id(data.id, db.inner()).await?;
|
||||
let mut model = model.into_active_model();
|
||||
let name_value: String = data.value;
|
||||
model.name = ActiveValue::Set(name_value);
|
||||
model.save(db.inner()).await?;
|
||||
Ok(())
|
||||
}
|
||||
#[post("/edit-task", data = "<data>")]
|
||||
pub async fn task_edit(
|
||||
db: &State<DatabaseConnection>,
|
||||
data: Form<IdValueForm<String>>,
|
||||
) -> Result<(), ResponderError> {
|
||||
let data = data.into_inner();
|
||||
let model = get_task_by_id(data.id, db.inner()).await?;
|
||||
let mut model = model.into_active_model();
|
||||
let description: String = data.value;
|
||||
model.description = ActiveValue::Set(Some(description));
|
||||
model.save(db.inner()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
pub(super) async fn service(db: &State<DatabaseConnection>) -> Result<Template, ResponderError> {
|
||||
pub(super) async fn service_index(
|
||||
db: &State<DatabaseConnection>,
|
||||
) -> Result<Template, ResponderError> {
|
||||
let services = get_services(db.inner()).await?;
|
||||
|
||||
Ok(Template::render(
|
||||
@@ -80,6 +115,15 @@ pub async fn increment_task_progress(
|
||||
db: &State<DatabaseConnection>,
|
||||
) -> Result<(), ResponderError> {
|
||||
let db = db.inner();
|
||||
let task = get_task_by_id(task, db).await?;
|
||||
let progress = task.progress;
|
||||
let mut task = task.into_active_model();
|
||||
task.progress = ActiveValue::Set(progress + 1);
|
||||
task.save(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_task_by_id(task: i32, db: &DatabaseConnection) -> Result<TasksModel, ResponderError> {
|
||||
let task = Tasks::find_by_id(task)
|
||||
.one(db)
|
||||
.await?
|
||||
@@ -87,12 +131,9 @@ pub async fn increment_task_progress(
|
||||
table: "Tasks",
|
||||
key: format!("{task}"),
|
||||
})?;
|
||||
let progress = task.progress;
|
||||
let mut task = task.into_active_model();
|
||||
task.progress = ActiveValue::Set(progress + 1);
|
||||
task.save(db).await?;
|
||||
Ok(())
|
||||
Ok(task)
|
||||
}
|
||||
|
||||
#[post("/<service>/increment-progress/<task>")]
|
||||
pub async fn increment_progress(
|
||||
service: i32,
|
||||
@@ -101,14 +142,7 @@ pub async fn increment_progress(
|
||||
) -> Result<(), ResponderError> {
|
||||
let db = db_state.inner();
|
||||
|
||||
let service =
|
||||
Services::find_by_id(service)
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or(ResponderError::DbEntityNotFound {
|
||||
table: "Services",
|
||||
key: format!("{service}"),
|
||||
})?;
|
||||
let service = get_service_by_id(service, db).await?;
|
||||
|
||||
let datetime = chrono::offset::Utc::now().to_rfc3339();
|
||||
|
||||
@@ -120,6 +154,22 @@ pub async fn increment_progress(
|
||||
service.save(db).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_service_by_id(
|
||||
service: i32,
|
||||
db: &DatabaseConnection,
|
||||
) -> Result<ServicesModel, ResponderError> {
|
||||
let service =
|
||||
Services::find_by_id(service)
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or(ResponderError::DbEntityNotFound {
|
||||
table: "Services",
|
||||
key: format!("{service}"),
|
||||
})?;
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
#[get("/update_progress")]
|
||||
pub async fn update_progress(db: &State<DatabaseConnection>) -> Result<Template, ResponderError> {
|
||||
let services = get_services(db.inner()).await?;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Services and Tasks</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -13,14 +14,62 @@
|
||||
<div class="row" id="services-container">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal" id="editIdStringModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Edit</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form id="editForm" method="post" action="/services/edit-service/test">
|
||||
<input type="hidden" id="idField" name="id">
|
||||
<div class="form-group">
|
||||
<label for="valueField">Name:</label>
|
||||
<input type="text" class="form-control" id="valueField" name="value" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" id="add-service">Add Service</button>
|
||||
<div style="flex-direction: row; display: flex" class="container">
|
||||
<button class="btn btn-primary" id="update-button">Update Progress</button>
|
||||
<div id="last-update-element" data-timestamp="0">hi</div>
|
||||
<input type="checkbox" id="auto-update-toggle" checked>
|
||||
<label for="auto-update-toggle">Enable Auto Update</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function openEditServiceModal(serviceId, serviceName) {
|
||||
setAutoUpdateActive(false);
|
||||
$('#idField').val(serviceId);
|
||||
$('#valueField').val(serviceName);
|
||||
$('#valueField').name = "Name";
|
||||
$('#editForm').attr("action", "/services/edit-service/");
|
||||
$('#editIdStringModal').modal('show');
|
||||
console.log('Edit Service ID: ' + serviceId);
|
||||
}
|
||||
|
||||
function openEditTaskModal(taskId, taskName) {
|
||||
setAutoUpdateActive(false);
|
||||
$('#idField').val(taskId);
|
||||
$('#valueField').val(taskName);
|
||||
$('#valueField').name = "Description";
|
||||
$('#editForm').attr("action", "/services/edit-task/");
|
||||
$('#editIdStringModal').modal('show');
|
||||
console.log('Edit Task ID: ' + taskId);
|
||||
}
|
||||
|
||||
function add_service() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
@@ -65,35 +114,91 @@
|
||||
console.log('updating progress: ' + lastUpdateElement.text());
|
||||
}
|
||||
|
||||
|
||||
// Auto-update functionality
|
||||
const normalUpdateInterval = 5000;
|
||||
const slowUpdateInterval = 30000;
|
||||
let autoUpdateCheckboxValue = true;
|
||||
let autoUpdateActiveOverride = autoUpdateCheckboxValue;
|
||||
let updateInterval;
|
||||
|
||||
function startUpdateInterval(interval) {
|
||||
clearInterval(updateInterval); // Clear any existing interval
|
||||
clearInterval(updateInterval);
|
||||
updateInterval = setInterval(updateProgress, interval);
|
||||
}
|
||||
|
||||
function stopUpdateInterval() {
|
||||
clearInterval(updateInterval);
|
||||
}
|
||||
|
||||
function setAutoUpdateActive(value) {
|
||||
autoUpdateActiveOverride = value;
|
||||
console.log("autoUpdateActive: " + autoUpdateActiveOverride);
|
||||
|
||||
if (autoUpdateActiveOverride) {
|
||||
// Checkbox is checked, start auto-update
|
||||
startUpdateInterval(normalUpdateInterval);
|
||||
} else {
|
||||
// Checkbox is unchecked, stop auto-update
|
||||
stopUpdateInterval();
|
||||
}
|
||||
}
|
||||
|
||||
function reloadAutoUpdateActiveFromCheckbox() {
|
||||
setAutoUpdateActive(autoUpdateCheckboxValue);
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
const normalUpdateInterval = 5000;
|
||||
const slowUpdateInterval = 30000;
|
||||
|
||||
|
||||
$("#update-button").click(updateProgress);
|
||||
$("#add-service").click(add_service);
|
||||
updateProgress();
|
||||
|
||||
|
||||
// Initial update and fast interval
|
||||
updateProgress();
|
||||
startUpdateInterval(normalUpdateInterval);
|
||||
|
||||
// Detect visibility changes
|
||||
$('#editForm').submit(function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var form = $(this);
|
||||
var url = form.attr('action');
|
||||
var type = form.attr('method');
|
||||
|
||||
$.ajax({
|
||||
type: type,
|
||||
url: url,
|
||||
data: form.serialize(),
|
||||
success: function (data) {
|
||||
$('#editIdStringModal').modal('hide');
|
||||
updateProgress();
|
||||
},
|
||||
error: function (error) {
|
||||
console.error("Error editing:", error);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
// Toggle auto-update
|
||||
$("#auto-update-toggle").change(function () {
|
||||
autoUpdateCheckboxValue = this.checked;
|
||||
reloadAutoUpdateActiveFromCheckbox();
|
||||
});
|
||||
autoUpdateCheckboxValue = $("#auto-update-toggle").is(":checked");
|
||||
$('#editIdStringModal').on('hidden.bs.modal', function () {
|
||||
reloadAutoUpdateActiveFromCheckbox();
|
||||
});
|
||||
|
||||
// Detect visibility changes (adjust interval when tab is hidden)
|
||||
document.addEventListener("visibilitychange", function () {
|
||||
if (document.visibilityState === "visible") {
|
||||
// Fast updates when tab is visible
|
||||
startUpdateInterval(normalUpdateInterval);
|
||||
} else {
|
||||
// Slow updates when tab is hidden
|
||||
startUpdateInterval(slowUpdateInterval);
|
||||
if (autoUpdateActiveOverride) {
|
||||
if (document.visibilityState === "visible") {
|
||||
startUpdateInterval(normalUpdateInterval);
|
||||
} else {
|
||||
startUpdateInterval(slowUpdateInterval);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
{% for service in services %}
|
||||
<div class="col-md-6">
|
||||
<h2>{{ service.name }}</h2>
|
||||
<h2>{{ service.name }}
|
||||
<button class="btn btn-sm btn-primary" onclick="openEditServiceModal('{{ service.id }}', '{{ service.name }}')">Edit</button>
|
||||
</h2>
|
||||
<ul class="list-group">
|
||||
{% for task in service.tasks %}
|
||||
<li class="list-group-item">
|
||||
{{ task.name }}
|
||||
{{ task.description }}
|
||||
<button class="btn btn-sm btn-primary" onclick="openEditTaskModal('{{ task.id }}', '{{ task.description }}')">Edit</button>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" style="width:{{ task.max_progress / task.progress * 100 }}%;" aria-valuenow="{{ task.progress }}" aria-valuemin="0" aria-valuemax="{{ task.max_progress }}">{{ task.progress }}%</div>
|
||||
<div class="progress-bar" role="progressbar" style="width:{{ task.progress/task.max_progress *100 }}%;" aria-valuenow="{{ task.progress }}" aria-valuemin="0" aria-valuemax="{{ task.max_progress }}">{{ task.progress }}%</div>
|
||||
</div>
|
||||
<button id="increment-button" onclick="update_task_progress( '{{service.id}}','{{task.id}}' )">+</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user