The portal's v2 API replaced nested categories (proclamation_categories.parent_id)
with flat categories plus tag-like groups: a proclamation now belongs to one
category and optionally one group. This lets the Proclamation Options page drop
its special-cased two-level tree.
Database:
- Add the proclamation_groups table (id, name, sort_order) and a
proclamations.group_id column.
- Drop parent_id from the proclamation_categories DDL so fresh databases get a
flat table.
- runMigrations() creates the new table/column, then runs a one-time
transactional v1->v2 migration keyed on PRAGMA user_version: every legacy
sub-category becomes a group (its id is preserved), proclamations that
pointed at a sub-category are remapped to (parent category, new group), and
the now-stale sub-category rows are deleted. selected flags are never
touched, so user choices survive. No network is needed, so offline kiosks
migrate at startup; re-running is a no-op.
- Add replaceGroups() and getAllGroups(); stop reading/writing parent_id.
API sync:
- Add PATH_GROUPS ("/groups") and parseGroupsJson().
- parseProclamationsJson() now also reads groupId.
- syncProclamationsFromApi() fetches /proclamations, /categories and /groups,
leaves the database untouched if any request or parse fails, then replaces
categories, replaces groups and upserts proclamations.
Proclamation Options page:
- FlatItem is now Category / Group / Proclamation.
- Each category lists only the groups that have at least one member in that
category, so impossible pairings (e.g. Physical/Prayers) never render.
- Proclamations with no group, or with a group the portal has removed, sit
directly under the category. Groups are collapsible and use a distinct color.
Tests:
- test_db: groups round-trip plus a legacy->v2 migration test asserting the
group is created, proclamations are remapped, selection is preserved and a
second initialize() is a no-op.
- test_proclamation_sync / test_api_client: parse groupId, parse groups, and
the live smoke test now also fetches /groups.
Docs: README database table and AGENTS notes updated for groups.
This commit is contained in:
@@ -16,13 +16,23 @@ struct ProclamationRecord {
|
||||
bool is_active = true;
|
||||
bool selected = false;
|
||||
int category_id = 0; // maps to ProclamationCategoryRecord::id
|
||||
int group_id = 0; // maps to ProclamationGroupRecord::id (0 = ungrouped)
|
||||
int solfeggio_frequency_id = 0; // maps to the solfeggio frequency preset
|
||||
};
|
||||
|
||||
struct ProclamationCategoryRecord {
|
||||
int id = 0;
|
||||
std::string name;
|
||||
int parent_id = 0; // 0 = root category
|
||||
int sort_order = 0;
|
||||
};
|
||||
|
||||
// A group is a tag-like organizer: it is not owned by a category. A
|
||||
// proclamation belongs to one category and may additionally belong to one
|
||||
// group. Within the UI, a group appears under a category only when at least
|
||||
// one proclamation pairs that (category_id, group_id).
|
||||
struct ProclamationGroupRecord {
|
||||
int id = 0;
|
||||
std::string name;
|
||||
int sort_order = 0;
|
||||
};
|
||||
|
||||
@@ -90,6 +100,9 @@ public:
|
||||
// Full sync: replaces all categories with the given set.
|
||||
bool replaceCategories(const std::vector<ProclamationCategoryRecord>& records);
|
||||
|
||||
// Full sync: replaces all groups with the given set.
|
||||
bool replaceGroups(const std::vector<ProclamationGroupRecord>& records);
|
||||
|
||||
// Full sync: upserts proclamations, deletes any not in `records`,
|
||||
// preserves the existing `selected` state, and forces the proclamation
|
||||
// identified by `defaultProclamationId` to remain selected.
|
||||
@@ -102,6 +115,9 @@ public:
|
||||
// Fetch all categories ordered by sort_order then id.
|
||||
std::vector<ProclamationCategoryRecord> getAllCategories();
|
||||
|
||||
// Fetch all groups ordered by sort_order then id.
|
||||
std::vector<ProclamationGroupRecord> getAllGroups();
|
||||
|
||||
// Toggle a single proclamation's selected flag.
|
||||
bool setProclamationSelected(const std::string& id, bool selected);
|
||||
|
||||
@@ -144,6 +160,7 @@ public:
|
||||
private:
|
||||
std::string resolvePath(const std::string& raw_path) const;
|
||||
bool runMigrations();
|
||||
bool migrateSubCategoriesToGroups();
|
||||
bool executeSql(const std::string& sql);
|
||||
bool columnExists(const std::string& table, const std::string& column);
|
||||
bool ensureColumn(const std::string& table, const std::string& column,
|
||||
|
||||
Reference in New Issue
Block a user