modify itemlist interactivity

This commit is contained in:
Sam Hadow 2024-02-21 01:17:15 +01:00
parent b9285fe97e
commit 708b625fbe
9 changed files with 311 additions and 44 deletions

View File

@ -108,6 +108,44 @@ def deleteList():
except: except:
return str('item not found in list'), 400 return str('item not found in list'), 400
@app.route('/app/change-list-name', methods=['POST'])
def changeListName():
data = request.get_json()
name = data.get('name')
uuid = data.get('uuid')
try:
change_list_name(uuid, name)
return jsonify({'name changed': uuid}), 200
except:
return str('error changing name'), 400
@app.route('/app/change-list-description', methods=['POST'])
def changeListDescription():
data = request.get_json()
description = data.get('description')
uuid = data.get('uuid')
try:
change_list_description(uuid, description)
return jsonify({'name changed': uuid}), 200
except:
return str('error changing description'), 400
@app.route('/app/change-list-content', methods=['POST'])
def changeListContent():
data = request.get_json()
uuid = data.get('uuid')
new = data.get('content')
try:
remove_all_from_list(uuid)
if len(new)>0 :
for item in new.split('#'):
add_to_list(uuid, item)
return jsonify({'content changed': uuid}), 200
except:
return str('error changing content'), 400
@app.route('/app/datahistory',methods = ['GET']) @app.route('/app/datahistory',methods = ['GET'])
def data_history_request(): def data_history_request():
if request.method == 'GET': if request.method == 'GET':
@ -142,11 +180,17 @@ def data_history_request_filtered():
lists_uuid = [] lists_uuid = []
return jsonify(get_history_filtered(lists_uuid)) return jsonify(get_history_filtered(lists_uuid))
@app.route('/app/datalist',methods = ['GET']) @app.route('/app/datalist',methods = ['GET', 'POST'])
def data_list_request(): def data_list_request():
if request.method == 'GET': if request.method == 'GET':
print("fetching data item") print("fetching data item")
return jsonify(get_lists()) return jsonify(get_lists())
elif request.method == 'POST':
data = request.get_json()
list_uuid = data.get('uuid')
details = get_list(list_uuid)
return jsonify(details), 200
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug = True) app.run(debug = True)

View File

@ -304,6 +304,19 @@ def remove_from_list(list_uuid, item_uuid):
connection.commit() connection.commit()
connection.close() connection.close()
def remove_all_from_list(list_uuid):
'''remove every items from an itemlist'''
connection = connect_db()
cursor = connection.cursor()
cursor.execute("""
DELETE
FROM listcontent
WHERE list_uuid = %s
""", (list_uuid,))
cursor.close()
connection.commit()
connection.close()
def get_lists(): def get_lists():
connection = connect_db() connection = connect_db()
cursor = connection.cursor() cursor = connection.cursor()
@ -316,6 +329,46 @@ def get_lists():
connection.close() connection.close()
return results return results
def get_list(list_uuid):
'''return details of a single list'''
connection = connect_db()
cursor = connection.cursor()
cursor.execute("""
SELECT name, description
FROM itemlist
WHERE uuid = %s
""", (list_uuid,))
results = cursor.fetchall()
cursor.close()
connection.close()
return results[0]
def change_list_name(uuid, name):
'''modify itemlist name'''
connection = connect_db()
cursor = connection.cursor()
cursor.execute("""
UPDATE itemlist
SET name = %s
WHERE uuid = %s
""", (name, uuid))
cursor.close()
connection.commit()
connection.close()
def change_list_description(uuid, description):
'''modify itemlist description'''
connection = connect_db()
cursor = connection.cursor()
cursor.execute("""
UPDATE itemlist
SET description = %s
WHERE uuid = %s
""", (description, uuid))
cursor.close()
connection.commit()
connection.close()
def export_csv(): def export_csv():
'''join item and history data from database and export it in ./output.csv''' '''join item and history data from database and export it in ./output.csv'''
connection = connect_db() connection = connect_db()

View File

@ -60,7 +60,7 @@
<button id="create_list_add" type="button" class="btn btn-secondary">add</button> <button id="create_list_add" type="button" class="btn btn-secondary">add</button>
<button id="create_list_cancel" type="button" class="btn btn-secondary">cancel</button> <button id="create_list_cancel" type="button" class="btn btn-secondary">cancel</button>
<p>Include items</p> <p>Include items</p>
<div id="checkbox-container"></div> <div id="checkbox-container" class="item_checkboxes"></div>
</div> </div>
</div> </div>
@ -69,15 +69,19 @@
<div class="popup-content"> <div class="popup-content">
<p>Modify list</p> <p>Modify list</p>
<div class="dropdown btn-group mr-2" id="modifylist_dropdown"> <div class="dropdown btn-group mr-2" id="modifylist_dropdown">
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown"> <button id="modify_itemlist_dropdown" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown">
Itemlist Itemlist
</button> </button>
<ul id="modify_itemlist_choice" class="dropdown-menu"> <ul id="modify_itemlist_choice" class="dropdown-menu">
</ul> </ul>
</div> </div>
</div>
<div id="modifylist_content"> <div id="modifylist_content">
</div> </div>
<div id="modifylist_buttons">
<button id="modifylist_ok" type="button" class="btn btn-secondary">ok</button>
<button id="modifylist_cancel" type="button" class="btn btn-secondary">cancel</button>
</div>
</div>
</div> </div>
<!-- popup delete list --> <!-- popup delete list -->
@ -104,6 +108,7 @@
<!-- Load scripts--> <!-- Load scripts-->
<script src="{{ url_for('static', filename='fetch.js') }}"></script> <script src="{{ url_for('static', filename='fetch.js') }}"></script>
<script src="{{ url_for('static', filename='filters_checkboxes.js') }}"></script>
<script src="{{ url_for('static', filename='popups.js') }}"></script> <script src="{{ url_for('static', filename='popups.js') }}"></script>
<script src="{{ url_for('static', filename='rendergraphs.js') }}"></script> <script src="{{ url_for('static', filename='rendergraphs.js') }}"></script>
<script src="{{ url_for('static', filename='list.js') }}"></script> <script src="{{ url_for('static', filename='list.js') }}"></script>

View File

@ -52,28 +52,8 @@ function refresh_graphs() {
render_graphs_wrapper(); render_graphs_wrapper();
// filters checkboxes // filters checkboxes
function filters_checkboxes() {
fetch_list().then(function(data){
{
const node = document.getElementById("filters_checkboxes");
while (node.firstChild) {
node.removeChild(node.lastChild);
}
}
const node = d3.select("#filters_checkboxes");
for (const itemlist of data) {
var label = node.append("label");
var input = label.append("input")
.attr("type", "checkbox")
.attr("class", "checkbox-filters")
.attr("id", `checkbox-${itemlist.uuid}`);
label.append("span")
.text(`${itemlist.name}`);
node.append("br");
}
})
}
filters_checkboxes() filters_checkboxes()
// listen to filters changes // listen to filters changes
var checkedBefore = getCheckedCheckboxIds('.checkbox-filters'); var checkedBefore = getCheckedCheckboxIds('.checkbox-filters');
$(document).ready(function(){ $(document).ready(function(){

View File

@ -181,3 +181,40 @@ async function get_data_filtered(id_array) {
history_data = await fetch_history_filtered(id_array); history_data = await fetch_history_filtered(id_array);
return Array(items_data, history_data); return Array(items_data, history_data);
} }
async function get_list_details(list_uuid) {
try {
// SELECT name, description
const apiUrl = `${currentUrl}app/datalist`;
const postData = {
uuid: list_uuid
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData)
};
const response = await fetch(apiUrl, requestOptions)
.catch(error => {
console.error('Error during POST request:', error);
});
if (response.ok) {
const rawData = await response.json();
const listData = {
name: rawData[0],
description: rawData[1],
};
return listData;
} else {
throw new Error('Error in server response');
}
} catch (error) {
console.error('Error fetching data item: ', error);
throw error;
}
}

22
web/filters_checkboxes.js Normal file
View File

@ -0,0 +1,22 @@
function filters_checkboxes() {
console.log("refresh filters");
fetch_list().then(function(data){
{
const node = document.getElementById("filters_checkboxes");
while (node.firstChild) {
node.removeChild(node.lastChild);
}
}
const node = d3.select("#filters_checkboxes");
for (const itemlist of data) {
var label = node.append("label");
var input = label.append("input")
.attr("type", "checkbox")
.attr("class", "checkbox-filters")
.attr("id", `checkbox-${itemlist.uuid}`);
label.append("span")
.text(`${itemlist.name}`);
node.append("br");
}
})
}

View File

@ -23,3 +23,85 @@ async function delItemlist(uuid) {
throw new Error('Error in server response'); throw new Error('Error in server response');
} }
} }
async function changeListName(uuid, new_name) {
const apiUrl = `${currentUrl}app/change-list-name`;
const postData = {
uuid: uuid,
name: new_name,
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData)
};
const response = await fetch(apiUrl, requestOptions)
.catch(error => {
console.error('Error during POST request:', error);
});
if (response.ok) {
console.log(response);
} else {
throw new Error('Error in server response');
}
}
async function changeListDescription(uuid, new_description) {
const apiUrl = `${currentUrl}app/change-list-description`;
const postData = {
uuid: uuid,
description: new_description,
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData)
};
const response = await fetch(apiUrl, requestOptions)
.catch(error => {
console.error('Error during POST request:', error);
});
if (response.ok) {
console.log(response);
} else {
throw new Error('Error in server response');
}
}
async function changeListContent(uuid, new_content) {
const apiUrl = `${currentUrl}app/change-list-content`;
const content = new_content.join('#');
const postData = {
uuid: uuid,
content: content,
};
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postData)
};
const response = await fetch(apiUrl, requestOptions)
.catch(error => {
console.error('Error during POST request:', error);
});
if (response.ok) {
console.log(response);
} else {
throw new Error('Error in server response');
}
}

View File

@ -50,8 +50,6 @@ document.getElementById("create_list_cancel").addEventListener("click", function
createListPopup.style.display = 'none'; createListPopup.style.display = 'none';
}); });
// modify list
// delete list // delete list
document.getElementById("delete_list").addEventListener("click", function () { document.getElementById("delete_list").addEventListener("click", function () {
// remove div content // remove div content
@ -79,11 +77,11 @@ document.getElementById("delete_list").addEventListener("click", function () {
deleteListPopup.style.display = 'flex'; deleteListPopup.style.display = 'flex';
}); });
// confirm delete list // confirm delete list
document.getElementById("delete_list_ok").addEventListener("click", function () { document.getElementById("delete_list_ok").addEventListener("click", async function () {
const checked_delete = getCheckedCheckboxIds('.checkbox-delete-list'); const checked_delete = getCheckedCheckboxIds('.checkbox-delete-list');
const id_array = checked_delete.map(str => str.substring(9)); const id_array = checked_delete.map(str => str.substring(9));
for (const uuid of id_array) { for (const uuid of id_array) {
delItemlist(uuid); await delItemlist(uuid);
} }
// refresh filters checkboxes // refresh filters checkboxes
filters_checkboxes(); filters_checkboxes();
@ -95,11 +93,16 @@ document.getElementById("delete_list_cancel").addEventListener("click", function
}); });
// modify list popup // modify list popup
var previous_modify = Object();
document.getElementById("modify_list").addEventListener("click", function () { document.getElementById("modify_list").addEventListener("click", function () {
generate_modifylist_buttons(); generate_modifylist_buttons();
// //
modifyListPopup.style.display = 'flex'; modifyListPopup.style.display = 'flex';
}); });
// cancel list modify
document.getElementById("modifylist_cancel").addEventListener("click", function () {
modifyListPopup.style.display = 'none';
});
function generate_modifylist_buttons() { function generate_modifylist_buttons() {
// remove div content // remove div content
const node = document.getElementById("modify_itemlist_choice"); const node = document.getElementById("modify_itemlist_choice");
@ -125,24 +128,36 @@ function generate_modifylist_buttons() {
}); });
} }
async function toggle_modify(itemlist_uuid) { async function toggle_modify(itemlist_uuid) {
// <div id="modifylist_content">
// <input id="listName" type="text" placeholder="name">
// <input id="listDescription" type="text" placeholder="description">
// <button id="create_list_add" type="button" class="btn btn-secondary">add</button>
// <button id="create_list_cancel" type="button" class="btn btn-secondary">cancel</button>
// <p>Include items</p>
// <div id="checkbox-container"></div>
// remove div content // remove div content
const node = document.getElementById("modifylist_content"); const node = document.getElementById("modifylist_content");
while (node.firstChild) { while (node.firstChild) {
node.removeChild(node.lastChild); node.removeChild(node.lastChild);
} }
// input name // new content
// input description previous_modify.uuid = itemlist_uuid;
// content
const existing_content = Object.keys(await fetch_item_filtered([itemlist_uuid])); const existing_content = Object.keys(await fetch_item_filtered([itemlist_uuid]));
previous_modify.content = existing_content;
const list_data = await get_list_details(itemlist_uuid);
const modify_content = d3.select("#modifylist_content"); const modify_content = d3.select("#modifylist_content");
const checkboxContainer = modify_content.append("div"); // dropdown menu text
document.getElementById("modify_itemlist_dropdown").innerHTML = `${list_data.name}`;
// input name
previous_modify.name = list_data.name;
const input_name = modify_content.append("input")
.attr("type", "text")
.attr("placeholder", "name")
.attr("value", `${list_data.name}`)
.attr("id", `input_name-${itemlist_uuid}`);
// input description
const input_description = modify_content.append("input")
.attr("type", "text")
.attr("placeholder", "description")
.attr("value", `${list_data.description}`)
.attr("id", `input_description-${itemlist_uuid}`);
previous_modify.description = list_data.description;
// content
const checkboxContainer = modify_content.append("div")
.attr("class", "item_checkboxes");
fetch_item().then(function(data){ fetch_item().then(function(data){
Object.keys(data).forEach(uuid => { Object.keys(data).forEach(uuid => {
const item = data[uuid]; const item = data[uuid];
@ -150,7 +165,7 @@ async function toggle_modify(itemlist_uuid) {
const checkbox = div.append("input") const checkbox = div.append("input")
.attr("type", "checkbox") .attr("type", "checkbox")
.attr("class", "checkbox-itemlist") .attr("class", "checkbox-modify-itemlist")
.attr("id", `checkbox-${uuid}`); .attr("id", `checkbox-${uuid}`);
if (existing_content.includes(`${uuid}`)) { if (existing_content.includes(`${uuid}`)) {
checkbox.property("checked", true); checkbox.property("checked", true);
@ -167,6 +182,35 @@ async function toggle_modify(itemlist_uuid) {
}); });
}); });
} }
// confirm list modify
document.getElementById("modifylist_ok").addEventListener("click", async function () {
const uuid = previous_modify.uuid;
const new_name = document.getElementById(`input_name-${uuid}`).value;
const new_description = document.getElementById(`input_description-${uuid}`).value;
const checked = getCheckedCheckboxIds('.checkbox-modify-itemlist');
const new_content = checked.map(str => str.substring(9));
var diff = false;
if (new_name != previous_modify.name) {
// name different
diff = true;
await changeListName(uuid, new_name);
}
if (new_description != previous_modify.description) {
// description different
diff = true;
await changeListDescription(uuid, new_description);
}
if (!(new_content.every(item => previous_modify.content.includes(item)) && previous_modify.content.every(item => new_content.includes(item)))) {
// content different
diff = true;
await changeListContent(uuid, new_content);
}
if (diff) {
// refresh filters checkboxes
filters_checkboxes();
}
modifyListPopup.style.display = 'none';
});
// delete item, pop-up confirm // delete item, pop-up confirm
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {

View File

@ -42,14 +42,14 @@ body {
padding: 0px; padding: 0px;
} }
#checkbox-container { .item_checkboxes {
max-height: 50%; max-height: 50%;
overflow-y: auto; overflow-y: auto;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
#checkbox-container div { .item_checkboxes div {
margin-bottom: 10px; margin-bottom: 10px;
width: calc(50% - 10px); width: calc(50% - 10px);
display: flex; display: flex;