added redirect and ability to save checkboxes
This commit is contained in:
parent
397189c259
commit
e13de7c786
@ -1,4 +1,5 @@
|
|||||||
#include "crow.h"
|
#include "crow.h"
|
||||||
|
#include "utils.hpp"
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -44,7 +45,7 @@ map<string, string> Database::getStrMap(const string& sql){
|
|||||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
string key = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
string key = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
||||||
string value = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
string value = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||||
map[key] = value;
|
map[key] = utils::urlDecode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -22,7 +24,7 @@ public:
|
|||||||
|
|
||||||
std::set<std::string> getStrSet(const std::string& sql);
|
std::set<std::string> getStrSet(const std::string& sql);
|
||||||
|
|
||||||
std::map<std::string, std::string> Database::getStrMap(const std::string& sql);
|
std::map<std::string, std::string> getStrMap(const std::string& sql);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sqlite3_stmt* prepareStmt(const std::string& sql);
|
sqlite3_stmt* prepareStmt(const std::string& sql);
|
||||||
|
|||||||
17
src/main.cpp
17
src/main.cpp
@ -38,6 +38,23 @@ int main() {
|
|||||||
return crow::response(utils::loadFile("templates/" + file));
|
return crow::response(utils::loadFile("templates/" + file));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Static file redirector
|
||||||
|
CROW_ROUTE(app, "/redirect")
|
||||||
|
([](const crow::request& req) {
|
||||||
|
auto file_param = req.url_params.get("file");
|
||||||
|
if (!file_param) {
|
||||||
|
return crow::response(400, "Missing 'file' parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string filepath = "/templates/";
|
||||||
|
filepath += utils::urlDecode(file_param); // Optional: decode %20 etc.
|
||||||
|
|
||||||
|
crow::response res;
|
||||||
|
res.code = 204;
|
||||||
|
res.add_header("HX-Redirect", filepath);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
CROW_ROUTE(app, "/status")([] {
|
CROW_ROUTE(app, "/status")([] {
|
||||||
auto table = create_service_table();
|
auto table = create_service_table();
|
||||||
return crow::response{table.htmx()};
|
return crow::response{table.htmx()};
|
||||||
|
|||||||
@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const vector<string>& itemList){
|
HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const vector<string>& itemList, std::map<std::string, std::string>& data){
|
||||||
html += format("<h2>{}</h2>", id);
|
html += format("<h2>{}</h2>", id);
|
||||||
html += "<div class='grid'>";
|
html += "<div class='grid'>";
|
||||||
for (auto& item : itemList){
|
for (auto& item : itemList){
|
||||||
string item_id = utils::to_id_format(format("{}_{}", id, item));
|
string item_id = utils::to_id_format(format("{}_{}", id, item));
|
||||||
|
auto value = data.contains(item_id) ? data[item_id] : "";
|
||||||
html += format("<label>{}:<input type='text' name='{}'></label>", item, item_id);
|
html += format("<label>{}:<input type='text' name='{}' value='{}'></label>", item, item_id, value);
|
||||||
}
|
}
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,13 @@
|
|||||||
#include "HtmxObject.h"
|
#include "HtmxObject.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class HtmxShAttributeList : public HtmxObject {
|
class HtmxShAttributeList : public HtmxObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// create new item list
|
// create new item list
|
||||||
HtmxShAttributeList(const std::string& id, const std::vector<std::string>& itemList);
|
HtmxShAttributeList(const std::string& id, const std::vector<std::string>& itemList, std::map<std::string, std::string>& data);
|
||||||
|
|
||||||
// create new item list where each item as a value
|
// create new item list where each item as a value
|
||||||
HtmxShAttributeList(const std::string& id, const std::vector<std::pair<std::string, std::string>>& itemValueList);
|
HtmxShAttributeList(const std::string& id, const std::vector<std::pair<std::string, std::string>>& itemValueList);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes)
|
HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes, std::map<std::string, std::string>& data)
|
||||||
{
|
{
|
||||||
html += "<div class='section'>";
|
html += "<div class='section'>";
|
||||||
html += format("<h2>{}</h2>", id);
|
html += format("<h2>{}</h2>", id);
|
||||||
@ -14,8 +14,10 @@ HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes)
|
|||||||
|
|
||||||
for (size_t i = 0; i < nbrOfBoxes; i++)
|
for (size_t i = 0; i < nbrOfBoxes; i++)
|
||||||
{
|
{
|
||||||
string item_id = utils::to_id_format(format("{}_{}",id, i));
|
string item_id = utils::to_id_format(format("Checkbox_{}_{}",id, i));
|
||||||
html += format("<label class='monitor-box'><input type='checkbox' name='{}'></label>", item_id);
|
auto value = data.contains(item_id) && data[item_id] == "1" ? "checked" : "";
|
||||||
|
|
||||||
|
html += format("<label class='monitor-box'><input type='checkbox' name='{}' value='1' {}></label>", item_id, value);
|
||||||
|
|
||||||
if ( ((i + 1) % 3 == 0) && (i != 0) )
|
if ( ((i + 1) % 3 == 0) && (i != 0) )
|
||||||
{
|
{
|
||||||
@ -30,6 +32,6 @@ HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes)
|
|||||||
void HtmxShCondition::genIds(std::vector<std::string>& vec, std::string id, size_t nbrOfBoxes)
|
void HtmxShCondition::genIds(std::vector<std::string>& vec, std::string id, size_t nbrOfBoxes)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nbrOfBoxes; i++){
|
for (int i = 0; i < nbrOfBoxes; i++){
|
||||||
vec.push_back(utils::to_id_format(format("{}_{}",id, i)));
|
vec.push_back(utils::to_id_format(format("Checkbox_{}_{}",id, i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,12 +3,13 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#include "HtmxObject.h"
|
#include "HtmxObject.h"
|
||||||
|
|
||||||
class HtmxShCondition : public HtmxObject {
|
class HtmxShCondition : public HtmxObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HtmxShCondition(std::string id, size_t nbrOfBoxes);
|
HtmxShCondition(std::string id, size_t nbrOfBoxes, std::map<std::string, std::string>& data);
|
||||||
|
|
||||||
static void genIds(std::vector<std::string>& vec, std::string id, size_t nbrOfBoxes);
|
static void genIds(std::vector<std::string>& vec, std::string id, size_t nbrOfBoxes);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,14 +4,15 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
HtmxShItemList::HtmxShItemList(const std::string& id, const std::vector<std::string>& columns, size_t size){
|
HtmxShItemList::HtmxShItemList(const std::string& id, const std::vector<std::string>& columns, size_t size, std::map<std::string, std::string>& data){
|
||||||
html += "<div class='section'>";
|
html += "<div class='section'>";
|
||||||
html += format("<h2>{}</h2>", id);
|
html += format("<h2>{}</h2>", id);
|
||||||
html += format("<div class='grid grid-{}'>", columns.size());
|
html += format("<div class='grid grid-{}'>", columns.size());
|
||||||
for (size_t i = 0; i < size; i++){
|
for (size_t i = 0; i < size; i++){
|
||||||
for (auto& col : columns){
|
for (auto& col : columns){
|
||||||
string item_id = utils::to_id_format(format("{}_{}_{}", id, i, col));
|
string item_id = utils::to_id_format(format("{}_{}_{}", id, i, col));
|
||||||
html += format("<input type='text' name='{}' placeholder='{}'>", item_id, col);
|
auto value = data.contains(item_id) ? data[item_id] : "";
|
||||||
|
html += format("<input type='text' name='{}' placeholder='{}' value='{}'>", item_id, col, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,13 @@
|
|||||||
#include "HtmxObject.h"
|
#include "HtmxObject.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class HtmxShItemList : public HtmxObject {
|
class HtmxShItemList : public HtmxObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// create new item list,
|
// create new item list,
|
||||||
HtmxShItemList(const std::string& id, const std::vector<std::string>& columns, size_t size);
|
HtmxShItemList(const std::string& id, const std::vector<std::string>& columns, size_t size, std::map<std::string, std::string>& data);
|
||||||
|
|
||||||
// create new item list where each item as a value
|
// create new item list where each item as a value
|
||||||
HtmxShItemList(const std::string& id, const std::vector<std::pair<std::string, std::string>>& itemValueList);
|
HtmxShItemList(const std::string& id, const std::vector<std::pair<std::string, std::string>>& itemValueList);
|
||||||
|
|||||||
@ -51,16 +51,24 @@ void initApi(crow::SimpleApp& app)
|
|||||||
return rsp("Failed to create id of character");
|
return rsp("Failed to create id of character");
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<pair<const string&, const string&>> idValues;
|
vector<pair<const string, const string>> idValues;
|
||||||
idValues.reserve(ShadowrunCharacterForm::m_formIds.size());
|
idValues.reserve(ShadowrunCharacterForm::m_formIds.size());
|
||||||
|
|
||||||
|
auto checkboxes = std::set<string>(ShadowrunCharacterForm::m_checkboxIds.begin(), ShadowrunCharacterForm::m_checkboxIds.end());
|
||||||
for (auto& id : ShadowrunCharacterForm::m_formIds) {
|
for (auto& id : ShadowrunCharacterForm::m_formIds) {
|
||||||
auto data = params[id];
|
auto data = params[id];
|
||||||
if(!
|
if(!data.empty()){
|
||||||
data.empty()){
|
|
||||||
idValues.push_back(make_pair(id, data));
|
idValues.push_back(make_pair(id, data));
|
||||||
|
if (checkboxes.contains(id)){
|
||||||
|
checkboxes.erase(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the checkboxes
|
||||||
|
for (auto& checkbox : checkboxes){
|
||||||
|
idValues.push_back(make_pair(checkbox, "0"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!storeCharacterData(key, idValues)){
|
if (!storeCharacterData(key, idValues)){
|
||||||
CROW_LOG_ERROR << "Failed to store character data of " << name_data;
|
CROW_LOG_ERROR << "Failed to store character data of " << name_data;
|
||||||
@ -76,7 +84,7 @@ void initApi(crow::SimpleApp& app)
|
|||||||
|
|
||||||
auto data = getCharacterData(getKeyOfCharacter(name));
|
auto data = getCharacterData(getKeyOfCharacter(name));
|
||||||
|
|
||||||
return crow::response{ShadowrunCharacterForm().htmx()};
|
return crow::response{ShadowrunCharacterForm(data).htmx()};
|
||||||
});
|
});
|
||||||
|
|
||||||
CROW_ROUTE(app, "/api/shadowrun/character-list")
|
CROW_ROUTE(app, "/api/shadowrun/character-list")
|
||||||
@ -85,7 +93,6 @@ void initApi(crow::SimpleApp& app)
|
|||||||
|
|
||||||
// Simulated character database
|
// Simulated character database
|
||||||
auto characters = getCharacters();
|
auto characters = getCharacters();
|
||||||
|
|
||||||
html << "<form hx-get='/api/shadowrun/character-form' hx-target='#form-container' hx-params='*'>"
|
html << "<form hx-get='/api/shadowrun/character-form' hx-target='#form-container' hx-params='*'>"
|
||||||
<< "<label>Character Name: "
|
<< "<label>Character Name: "
|
||||||
<< "<select name='name'>";
|
<< "<select name='name'>";
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <format>
|
||||||
#include "HtmxShItemList.hpp"
|
#include "HtmxShItemList.hpp"
|
||||||
#include "HtmxShAttributeList.hpp"
|
#include "HtmxShAttributeList.hpp"
|
||||||
#include "HtmxShCondition.hpp"
|
#include "HtmxShCondition.hpp"
|
||||||
@ -76,6 +77,13 @@ static const vector<string> cArmorParamters = {
|
|||||||
"Notes",
|
"Notes",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const vector<string> genCheckboxIds(){
|
||||||
|
vector<string> vec;
|
||||||
|
HtmxShCondition::genIds(vec, "Physical Condition", 18);
|
||||||
|
HtmxShCondition::genIds(vec, "Stun Condition", 12);
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
static const vector<string> genFormIds(){
|
static const vector<string> genFormIds(){
|
||||||
vector<string> vec;
|
vector<string> vec;
|
||||||
vec.reserve(200);
|
vec.reserve(200);
|
||||||
@ -83,16 +91,20 @@ static const vector<string> genFormIds(){
|
|||||||
// OBS make sure to update both here and in ShadowrunCharacterForm()
|
// OBS make sure to update both here and in ShadowrunCharacterForm()
|
||||||
HtmxShAttributeList::genIds(vec, "Character Info", cCharacterInfo);
|
HtmxShAttributeList::genIds(vec, "Character Info", cCharacterInfo);
|
||||||
HtmxShAttributeList::genIds(vec, "Attributes", cAttributes);
|
HtmxShAttributeList::genIds(vec, "Attributes", cAttributes);
|
||||||
HtmxShItemList::genIds(vec, "Active Skills", cSkillParameters, 6);
|
HtmxShItemList::genIds(vec, "Active Skills", cSkillParameters, 8);
|
||||||
HtmxShItemList::genIds(vec, "Knowledge Skills", cSkillParameters, 6);
|
HtmxShItemList::genIds(vec, "Knowledge Skills", cSkillParameters, 8);
|
||||||
vec.push_back("positive_qualities");
|
vec.push_back("positive_qualities");
|
||||||
vec.push_back("negative_qualities");
|
vec.push_back("negative_qualities");
|
||||||
vec.push_back("datapack_notes");
|
vec.push_back("datapack_notes");
|
||||||
|
|
||||||
|
auto v = genCheckboxIds();
|
||||||
|
vec.insert(vec.end(), v.begin(), v.end());
|
||||||
|
|
||||||
HtmxShCondition::genIds(vec, "Physical Condition", 18);
|
HtmxShCondition::genIds(vec, "Physical Condition", 18);
|
||||||
HtmxShCondition::genIds(vec, "Stun Condition", 12);
|
HtmxShCondition::genIds(vec, "Stun Condition", 12);
|
||||||
HtmxShItemList::genIds(vec, "Contacts", cContactsParameters, 6);
|
HtmxShItemList::genIds(vec, "Contacts", cContactsParameters, 6);
|
||||||
HtmxShItemList::genIds(vec, "Ranged Weapons", cRangedWeaponsParameters, 7);
|
HtmxShItemList::genIds(vec, "Ranged Weapons", cRangedWeaponsParameters, 7);
|
||||||
HtmxShItemList::genIds(vec, "Cyberware and Bioware", cImplantParameters, 7);
|
HtmxShItemList::genIds(vec, "Cyberware and Bioware", cImplantParameters, 9);
|
||||||
HtmxShItemList::genIds(vec, "Melee Weapons", cMeleeWeaponParameters, 7);
|
HtmxShItemList::genIds(vec, "Melee Weapons", cMeleeWeaponParameters, 7);
|
||||||
HtmxShItemList::genIds(vec, "Armor", cArmorParamters , 3);
|
HtmxShItemList::genIds(vec, "Armor", cArmorParamters , 3);
|
||||||
|
|
||||||
@ -100,44 +112,49 @@ static const vector<string> genFormIds(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> ShadowrunCharacterForm::m_formIds = genFormIds();
|
const std::vector<std::string> ShadowrunCharacterForm::m_formIds = genFormIds();
|
||||||
|
const std::vector<std::string> ShadowrunCharacterForm::m_checkboxIds = genCheckboxIds();
|
||||||
|
|
||||||
ShadowrunCharacterForm::ShadowrunCharacterForm() {
|
ShadowrunCharacterForm::ShadowrunCharacterForm(std::map<std::string, std::string>& data) {
|
||||||
html.reserve(30000);
|
html.reserve(30000);
|
||||||
|
|
||||||
html += "<form hx-post='/api/shadowrun/submit-character' hx-target='#form-response' hx-swap='innerHTML'>";
|
html += "<form hx-post='/api/shadowrun/submit-character' hx-target='#form-response' hx-swap='innerHTML'>";
|
||||||
html += HtmxShAttributeList("Character Info", cCharacterInfo).htmx();
|
html += HtmxShAttributeList("Character Info", cCharacterInfo, data).htmx();
|
||||||
html += HtmxShAttributeList("Attributes", cAttributes).htmx();
|
html += HtmxShAttributeList("Attributes", cAttributes, data).htmx();
|
||||||
|
|
||||||
html += "<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 2em;'>";
|
html += "<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 2em;'>";
|
||||||
html += HtmxShItemList("Active Skills", cSkillParameters, 6).htmx();
|
html += HtmxShItemList("Active Skills", cSkillParameters, 8, data).htmx();
|
||||||
html += HtmxShItemList("Knowledge Skills", cSkillParameters, 6).htmx();
|
html += HtmxShItemList("Knowledge Skills", cSkillParameters, 8, data).htmx();
|
||||||
|
|
||||||
|
auto valuePos = data.contains("positive_qualities") ? data["positive_qualities"] : "";
|
||||||
|
auto valueNeg = data.contains("negative_qualities") ? data["negative_qualities"] : "";
|
||||||
|
|
||||||
// add Qualities
|
// add Qualities
|
||||||
html += "<div class='section'>"
|
html += format("<div class='section'>"
|
||||||
"<h2>Qualities</h2>"
|
"<h2>Qualities</h2>"
|
||||||
"<label>Positive Qualities:"
|
"<label>Positive Qualities:"
|
||||||
"<textarea name='positive_qualities' rows='4'></textarea>"
|
"<textarea name='positive_qualities' rows='4'>{}</textarea>"
|
||||||
"</label>"
|
"</label>"
|
||||||
"<label>Negative Qualities:"
|
"<label>Negative Qualities:"
|
||||||
"<textarea name='negative_qualities' rows='4'></textarea>"
|
"<textarea name='negative_qualities' rows='4'>{}</textarea>"
|
||||||
"</label>"
|
"</label>"
|
||||||
"</div>";
|
"</div>", valuePos, valueNeg);
|
||||||
|
|
||||||
|
auto valueNotes = data.contains("datapack_notes") ? data["datapack_notes"] : "";
|
||||||
// add datapack notes
|
// add datapack notes
|
||||||
html += "<div class='section'>"
|
html += format("<div class='section'>"
|
||||||
"<h2>Datajack / Commlink / Cyberdeck / Notes</h2>"
|
"<h2>Datajack / Commlink / Cyberdeck / Notes</h2>"
|
||||||
"<label>Notes:"
|
"<label>Notes:"
|
||||||
"<textarea name='datapack_notes' rows='6'></textarea>"
|
"<textarea name='datapack_notes' rows='6'>{}</textarea>"
|
||||||
"</label>"
|
"</label>"
|
||||||
"</div>";
|
"</div>", valueNotes);
|
||||||
|
|
||||||
html += HtmxShCondition("Physical Condition", 18).htmx();
|
html += HtmxShCondition("Physical Condition", 18, data).htmx();
|
||||||
html += HtmxShCondition("Stun Condition", 12).htmx();
|
html += HtmxShCondition("Stun Condition", 12, data).htmx();
|
||||||
html += HtmxShItemList("Contacts", cContactsParameters, 6).htmx();
|
html += HtmxShItemList("Contacts", cContactsParameters, 6, data).htmx();
|
||||||
html += HtmxShItemList("Ranged Weapons", cRangedWeaponsParameters, 7).htmx();
|
html += HtmxShItemList("Ranged Weapons", cRangedWeaponsParameters, 7, data).htmx();
|
||||||
html += HtmxShItemList("Cyberware and Bioware", cImplantParameters, 7).htmx();
|
html += HtmxShItemList("Cyberware and Bioware", cImplantParameters, 9, data).htmx();
|
||||||
html += HtmxShItemList("Melee Weapons", cMeleeWeaponParameters, 7).htmx();
|
html += HtmxShItemList("Melee Weapons", cMeleeWeaponParameters, 7, data).htmx();
|
||||||
html += HtmxShItemList("Armor", cArmorParamters , 3).htmx();
|
html += HtmxShItemList("Armor", cArmorParamters , 3, data).htmx();
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
|
|
||||||
html += "<div style='text-align:center'>"
|
html += "<div style='text-align:center'>"
|
||||||
|
|||||||
@ -2,13 +2,16 @@
|
|||||||
#define SHADOWRUN_CHARACTER_FORM_HPP
|
#define SHADOWRUN_CHARACTER_FORM_HPP
|
||||||
|
|
||||||
#include "htmx/HtmxObject.h"
|
#include "htmx/HtmxObject.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class ShadowrunCharacterForm : public HtmxObject {
|
class ShadowrunCharacterForm : public HtmxObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ShadowrunCharacterForm();
|
ShadowrunCharacterForm(std::map<std::string, std::string>& data);
|
||||||
|
|
||||||
static const std::vector<std::string> m_formIds;
|
static const std::vector<std::string> m_formIds;
|
||||||
|
static const std::vector<std::string> m_checkboxIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHADOWRUN_CHARACTER_FORM_HPP
|
#endif // SHADOWRUN_CHARACTER_FORM_HPP
|
||||||
@ -31,7 +31,7 @@ bool initDb() {
|
|||||||
"value TEXT,"
|
"value TEXT,"
|
||||||
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP,"
|
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP,"
|
||||||
"updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,"
|
"updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,"
|
||||||
"FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE);";
|
"FOREIGN KEY (character_id) REFERENCES shadowrun_characters(id) ON DELETE CASCADE);";
|
||||||
|
|
||||||
if (!db.exec(create_sql_data)){
|
if (!db.exec(create_sql_data)){
|
||||||
CROW_LOG_ERROR << "Failed to create shadowrun_data table";
|
CROW_LOG_ERROR << "Failed to create shadowrun_data table";
|
||||||
@ -41,7 +41,7 @@ bool initDb() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t getKeyOfCharacter(const string& name){
|
int64_t getKeyOfCharacter(const string& name){
|
||||||
auto sql = format("SELECT 1 FROM shadowrun_characters WHERE name = '{}' LIMIT 1;", name);
|
auto sql = format("SELECT id FROM shadowrun_characters WHERE name = '{}' LIMIT 1;", name);
|
||||||
auto db = Database();
|
auto db = Database();
|
||||||
|
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
@ -63,7 +63,7 @@ int64_t getKeyOfCharacter(const string& name){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool storeCharacterData(int64_t characterKey, vector<pair<const string&, const string&>>& idValues){
|
bool storeCharacterData(int64_t characterKey, vector<pair<const string, const string>>& idValues){
|
||||||
auto sql = format("SELECT name FROM shadowrun_data WHERE character_id = {};", characterKey);
|
auto sql = format("SELECT name FROM shadowrun_data WHERE character_id = {};", characterKey);
|
||||||
auto db = Database();
|
auto db = Database();
|
||||||
if (!db.open())
|
if (!db.open())
|
||||||
@ -74,13 +74,13 @@ bool storeCharacterData(int64_t characterKey, vector<pair<const string&, const s
|
|||||||
for (auto& idValue : idValues) {
|
for (auto& idValue : idValues) {
|
||||||
// update if already exist
|
// update if already exist
|
||||||
if(set.contains(idValue.first)){
|
if(set.contains(idValue.first)){
|
||||||
auto sql = format("UPDATE shadowrun_data SET value = {}, updated_at = CURRENT_TIMESTAMP WHERE name = {} AND character_id = {}", idValue.second, idValue.first, characterKey);
|
auto sql = format("UPDATE shadowrun_data SET value = '{}', updated_at = CURRENT_TIMESTAMP WHERE name = '{}' AND character_id = {}", idValue.second, idValue.first, characterKey);
|
||||||
if (!db.exec(sql)){
|
if (!db.exec(sql)){
|
||||||
CROW_LOG_WARNING << "Failed to update " << idValue.first << " with " << idValue.second;
|
CROW_LOG_WARNING << "Failed to update " << idValue.first << " with " << idValue.second;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto sql = format("INSERT INTO shadowrun_data (character_id, name, value)"
|
auto sql = format("INSERT INTO shadowrun_data (character_id, name, value) "
|
||||||
"VALUES ({}, {}, {})", characterKey, idValue.first, idValue.second);
|
"VALUES ({}, '{}', '{}')", characterKey, idValue.first, idValue.second);
|
||||||
if (!db.exec(sql)){
|
if (!db.exec(sql)){
|
||||||
CROW_LOG_WARNING << "Failed to insert " << idValue.first << " with " << idValue.second;
|
CROW_LOG_WARNING << "Failed to insert " << idValue.first << " with " << idValue.second;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ namespace shadowrun{
|
|||||||
|
|
||||||
bool initDb();
|
bool initDb();
|
||||||
int64_t getKeyOfCharacter(const std::string& name);
|
int64_t getKeyOfCharacter(const std::string& name);
|
||||||
bool storeCharacterData(int64_t characterKey, std::vector<std::pair<const std::string&, const std::string&>>& idValues);
|
bool storeCharacterData(int64_t characterKey, std::vector<std::pair<const std::string, const std::string>>& idValues);
|
||||||
std::set<std::string> getCharacters();
|
std::set<std::string> getCharacters();
|
||||||
|
|
||||||
std::map<std::string, std::string> getCharacterData(int64_t characterKey);
|
std::map<std::string, std::string> getCharacterData(int64_t characterKey);
|
||||||
|
|||||||
@ -76,4 +76,25 @@ std::filesystem::path getDataDir(){
|
|||||||
: std::filesystem::path(std::getenv("HOME")) / ".local" / "share" / APPLICATION_NAME;
|
: std::filesystem::path(std::getenv("HOME")) / ".local" / "share" / APPLICATION_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string urlDecode(const std::string& str) {
|
||||||
|
std::ostringstream decoded;
|
||||||
|
for (size_t i = 0; i < str.length(); ++i) {
|
||||||
|
if (str[i] == '%' && i + 2 < str.length()) {
|
||||||
|
std::istringstream hex_stream(str.substr(i + 1, 2));
|
||||||
|
int hex = 0;
|
||||||
|
if (hex_stream >> std::hex >> hex) {
|
||||||
|
decoded << static_cast<char>(hex);
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
decoded << '%'; // malformed encoding, keep as-is
|
||||||
|
}
|
||||||
|
} else if (str[i] == '+') {
|
||||||
|
decoded << ' '; // '+' is often used for spaces in form-encoding
|
||||||
|
} else {
|
||||||
|
decoded << str[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decoded.str();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -14,6 +14,8 @@ namespace utils {
|
|||||||
std::string loadFile(const std::string& path);
|
std::string loadFile(const std::string& path);
|
||||||
|
|
||||||
std::filesystem::path getDataDir();
|
std::filesystem::path getDataDir();
|
||||||
|
|
||||||
|
std::string urlDecode(const std::string& str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -36,9 +36,34 @@
|
|||||||
align-items: center; /* Optional: center items within the column */
|
align-items: center; /* Optional: center items within the column */
|
||||||
gap: 1rem; /* Space between elements */
|
gap: 1rem; /* Space between elements */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-panel {
|
||||||
|
display: inline-block;
|
||||||
|
width: 200px;
|
||||||
|
height: 120px;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
.app-panel:hover {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div class="app-panel"
|
||||||
|
hx-get="/redirect?file=shadowrun.html"
|
||||||
|
hx-trigger="click"
|
||||||
|
hx-target="this"
|
||||||
|
hx-swap="none">
|
||||||
|
<h3>Shadowrun</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h1>Service Status</h1>
|
<h1>Service Status</h1>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user