diff --git a/src/database/database.cpp b/src/database/database.cpp index 31db99c..08b7476 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -1,4 +1,5 @@ #include "crow.h" +#include "utils.hpp" #include "database.hpp" using namespace std; @@ -44,7 +45,7 @@ map Database::getStrMap(const string& sql){ while (sqlite3_step(stmt) == SQLITE_ROW) { string key = reinterpret_cast(sqlite3_column_text(stmt, 0)); string value = reinterpret_cast(sqlite3_column_text(stmt, 1)); - map[key] = value; + map[key] = utils::urlDecode(value); } sqlite3_finalize(stmt); diff --git a/src/database/database.hpp b/src/database/database.hpp index e6dcdaf..462d091 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -5,6 +5,8 @@ #include #include #include +#include + class Database { public: @@ -22,7 +24,7 @@ public: std::set getStrSet(const std::string& sql); - std::map Database::getStrMap(const std::string& sql); + std::map getStrMap(const std::string& sql); private: sqlite3_stmt* prepareStmt(const std::string& sql); diff --git a/src/main.cpp b/src/main.cpp index 0f636eb..658fc91 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,23 @@ int main() { 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")([] { auto table = create_service_table(); return crow::response{table.htmx()}; diff --git a/src/shadowrun/HtmxShAttributeList.cpp b/src/shadowrun/HtmxShAttributeList.cpp index d17bdff..ecd33eb 100644 --- a/src/shadowrun/HtmxShAttributeList.cpp +++ b/src/shadowrun/HtmxShAttributeList.cpp @@ -4,13 +4,13 @@ using namespace std; -HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const vector& itemList){ +HtmxShAttributeList::HtmxShAttributeList(const std::string& id, const vector& itemList, std::map& data){ html += format("

{}

", id); html += "
"; for (auto& item : itemList){ string item_id = utils::to_id_format(format("{}_{}", id, item)); - - html += format("", item, item_id); + auto value = data.contains(item_id) ? data[item_id] : ""; + html += format("", item, item_id, value); } html += "
"; } diff --git a/src/shadowrun/HtmxShAttributeList.hpp b/src/shadowrun/HtmxShAttributeList.hpp index 64f9377..effb473 100644 --- a/src/shadowrun/HtmxShAttributeList.hpp +++ b/src/shadowrun/HtmxShAttributeList.hpp @@ -4,12 +4,13 @@ #include "HtmxObject.h" #include #include +#include class HtmxShAttributeList : public HtmxObject { public: // create new item list - HtmxShAttributeList(const std::string& id, const std::vector& itemList); + HtmxShAttributeList(const std::string& id, const std::vector& itemList, std::map& data); // create new item list where each item as a value HtmxShAttributeList(const std::string& id, const std::vector>& itemValueList); diff --git a/src/shadowrun/HtmxShCondition.cpp b/src/shadowrun/HtmxShCondition.cpp index 8a65376..e620fdc 100644 --- a/src/shadowrun/HtmxShCondition.cpp +++ b/src/shadowrun/HtmxShCondition.cpp @@ -4,7 +4,7 @@ using namespace std; -HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes) +HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes, std::map& data) { html += "
"; html += format("

{}

", id); @@ -14,8 +14,10 @@ HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes) for (size_t i = 0; i < nbrOfBoxes; i++) { - string item_id = utils::to_id_format(format("{}_{}",id, i)); - html += format("", item_id); + string item_id = utils::to_id_format(format("Checkbox_{}_{}",id, i)); + auto value = data.contains(item_id) && data[item_id] == "1" ? "checked" : ""; + + html += format("", item_id, value); if ( ((i + 1) % 3 == 0) && (i != 0) ) { @@ -30,6 +32,6 @@ HtmxShCondition::HtmxShCondition(std::string id, size_t nbrOfBoxes) void HtmxShCondition::genIds(std::vector& vec, std::string id, size_t nbrOfBoxes) { 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))); } } diff --git a/src/shadowrun/HtmxShCondition.hpp b/src/shadowrun/HtmxShCondition.hpp index cba8415..fe5c8cc 100644 --- a/src/shadowrun/HtmxShCondition.hpp +++ b/src/shadowrun/HtmxShCondition.hpp @@ -3,12 +3,13 @@ #include #include +#include #include "HtmxObject.h" class HtmxShCondition : public HtmxObject { public: - HtmxShCondition(std::string id, size_t nbrOfBoxes); + HtmxShCondition(std::string id, size_t nbrOfBoxes, std::map& data); static void genIds(std::vector& vec, std::string id, size_t nbrOfBoxes); }; diff --git a/src/shadowrun/HtmxShItemList.cpp b/src/shadowrun/HtmxShItemList.cpp index 998c79f..5ff2f2d 100644 --- a/src/shadowrun/HtmxShItemList.cpp +++ b/src/shadowrun/HtmxShItemList.cpp @@ -4,14 +4,15 @@ using namespace std; -HtmxShItemList::HtmxShItemList(const std::string& id, const std::vector& columns, size_t size){ +HtmxShItemList::HtmxShItemList(const std::string& id, const std::vector& columns, size_t size, std::map& data){ html += "
"; html += format("

{}

", id); html += format("
", columns.size()); for (size_t i = 0; i < size; i++){ for (auto& col : columns){ string item_id = utils::to_id_format(format("{}_{}_{}", id, i, col)); - html += format("", item_id, col); + auto value = data.contains(item_id) ? data[item_id] : ""; + html += format("", item_id, col, value); } } diff --git a/src/shadowrun/HtmxShItemList.hpp b/src/shadowrun/HtmxShItemList.hpp index d231be8..186f0fd 100644 --- a/src/shadowrun/HtmxShItemList.hpp +++ b/src/shadowrun/HtmxShItemList.hpp @@ -4,12 +4,13 @@ #include "HtmxObject.h" #include #include +#include class HtmxShItemList : public HtmxObject { public: // create new item list, - HtmxShItemList(const std::string& id, const std::vector& columns, size_t size); + HtmxShItemList(const std::string& id, const std::vector& columns, size_t size, std::map& data); // create new item list where each item as a value HtmxShItemList(const std::string& id, const std::vector>& itemValueList); diff --git a/src/shadowrun/ShadowrunApi.cpp b/src/shadowrun/ShadowrunApi.cpp index 7d730b4..42cdcce 100644 --- a/src/shadowrun/ShadowrunApi.cpp +++ b/src/shadowrun/ShadowrunApi.cpp @@ -51,17 +51,25 @@ void initApi(crow::SimpleApp& app) return rsp("Failed to create id of character"); } - vector> idValues; + vector> idValues; idValues.reserve(ShadowrunCharacterForm::m_formIds.size()); + auto checkboxes = std::set(ShadowrunCharacterForm::m_checkboxIds.begin(), ShadowrunCharacterForm::m_checkboxIds.end()); for (auto& id : ShadowrunCharacterForm::m_formIds) { auto data = params[id]; - if(! - data.empty()){ + if(!data.empty()){ 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)){ CROW_LOG_ERROR << "Failed to store character data of " << name_data; return rsp("Failed to store character data"); @@ -76,7 +84,7 @@ void initApi(crow::SimpleApp& app) auto data = getCharacterData(getKeyOfCharacter(name)); - return crow::response{ShadowrunCharacterForm().htmx()}; + return crow::response{ShadowrunCharacterForm(data).htmx()}; }); CROW_ROUTE(app, "/api/shadowrun/character-list") @@ -85,7 +93,6 @@ void initApi(crow::SimpleApp& app) // Simulated character database auto characters = getCharacters(); - html << "
" << "" "" - "
"; + "
", valuePos, valueNeg); + auto valueNotes = data.contains("datapack_notes") ? data["datapack_notes"] : ""; // add datapack notes - html += "
" + html += format("
" "

Datajack / Commlink / Cyberdeck / Notes

" "" - "
"; + "
", valueNotes); - html += HtmxShCondition("Physical Condition", 18).htmx(); - html += HtmxShCondition("Stun Condition", 12).htmx(); - html += HtmxShItemList("Contacts", cContactsParameters, 6).htmx(); - html += HtmxShItemList("Ranged Weapons", cRangedWeaponsParameters, 7).htmx(); - html += HtmxShItemList("Cyberware and Bioware", cImplantParameters, 7).htmx(); - html += HtmxShItemList("Melee Weapons", cMeleeWeaponParameters, 7).htmx(); - html += HtmxShItemList("Armor", cArmorParamters , 3).htmx(); + html += HtmxShCondition("Physical Condition", 18, data).htmx(); + html += HtmxShCondition("Stun Condition", 12, data).htmx(); + html += HtmxShItemList("Contacts", cContactsParameters, 6, data).htmx(); + html += HtmxShItemList("Ranged Weapons", cRangedWeaponsParameters, 7, data).htmx(); + html += HtmxShItemList("Cyberware and Bioware", cImplantParameters, 9, data).htmx(); + html += HtmxShItemList("Melee Weapons", cMeleeWeaponParameters, 7, data).htmx(); + html += HtmxShItemList("Armor", cArmorParamters , 3, data).htmx(); html += "
"; html += "
" diff --git a/src/shadowrun/ShadowrunCharacterForm.hpp b/src/shadowrun/ShadowrunCharacterForm.hpp index 2fa8b01..9445ca2 100644 --- a/src/shadowrun/ShadowrunCharacterForm.hpp +++ b/src/shadowrun/ShadowrunCharacterForm.hpp @@ -2,13 +2,16 @@ #define SHADOWRUN_CHARACTER_FORM_HPP #include "htmx/HtmxObject.h" +#include +#include class ShadowrunCharacterForm : public HtmxObject { public: - ShadowrunCharacterForm(); + ShadowrunCharacterForm(std::map& data); static const std::vector m_formIds; + static const std::vector m_checkboxIds; }; #endif // SHADOWRUN_CHARACTER_FORM_HPP \ No newline at end of file diff --git a/src/shadowrun/ShadowrunDb.cpp b/src/shadowrun/ShadowrunDb.cpp index 9faf485..ea6b3f1 100644 --- a/src/shadowrun/ShadowrunDb.cpp +++ b/src/shadowrun/ShadowrunDb.cpp @@ -31,7 +31,7 @@ bool initDb() { "value TEXT," "created_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)){ CROW_LOG_ERROR << "Failed to create shadowrun_data table"; @@ -41,7 +41,7 @@ bool initDb() { } 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(); if (!db.open()) @@ -63,7 +63,7 @@ int64_t getKeyOfCharacter(const string& name){ } } -bool storeCharacterData(int64_t characterKey, vector>& idValues){ +bool storeCharacterData(int64_t characterKey, vector>& idValues){ auto sql = format("SELECT name FROM shadowrun_data WHERE character_id = {};", characterKey); auto db = Database(); if (!db.open()) @@ -74,13 +74,13 @@ bool storeCharacterData(int64_t characterKey, vector>& idValues); + bool storeCharacterData(int64_t characterKey, std::vector>& idValues); std::set getCharacters(); std::map getCharacterData(int64_t characterKey); diff --git a/src/utils.cpp b/src/utils.cpp index 3d375b5..078dd99 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -76,4 +76,25 @@ std::filesystem::path getDataDir(){ : 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(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(); +} + } \ No newline at end of file diff --git a/src/utils.hpp b/src/utils.hpp index 99e049a..55a51e1 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -14,6 +14,8 @@ namespace utils { std::string loadFile(const std::string& path); std::filesystem::path getDataDir(); + + std::string urlDecode(const std::string& str); } #endif diff --git a/templates/index.html b/templates/index.html index f657be2..eefaabc 100644 --- a/templates/index.html +++ b/templates/index.html @@ -36,9 +36,34 @@ align-items: center; /* Optional: center items within the column */ 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; + } + +
+

Shadowrun

+
+

Service Status