From d02f51e1e14c81272884dbb32e4776090142eb82 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 21 Jun 2026 10:54:47 -0500 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20BoardManagementHelper=20=E2=80=94?= =?UTF-8?q?=20member=20terms,=20committees,=20meeting=20attendance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Helper/BoardManagementHelper.php | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 source/packages/plg_system_mokosuitenpo/src/Helper/BoardManagementHelper.php diff --git a/source/packages/plg_system_mokosuitenpo/src/Helper/BoardManagementHelper.php b/source/packages/plg_system_mokosuitenpo/src/Helper/BoardManagementHelper.php new file mode 100644 index 0000000..a47ef9d --- /dev/null +++ b/source/packages/plg_system_mokosuitenpo/src/Helper/BoardManagementHelper.php @@ -0,0 +1,105 @@ +get(DatabaseInterface::class); + + $db->setQuery($db->getQuery(true) + ->select('bm.*, cd.name, cd.email_to, cd.telephone') + ->select('CASE WHEN bm.term_end < NOW() THEN ' . $db->quote('expired') + . ' WHEN bm.term_end < DATE_ADD(NOW(), INTERVAL 90 DAY) THEN ' . $db->quote('expiring_soon') + . ' ELSE ' . $db->quote('active') . ' END AS term_status') + ->from($db->quoteName('#__mokosuitenpo_board_members', 'bm')) + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = bm.contact_id') + ->where($db->quoteName('bm.status') . ' = ' . $db->quote('active')) + ->order('bm.role ASC, cd.name ASC')); + + return $db->loadObjectList() ?: []; + } + + /** + * Get committee assignments. + */ + public static function getCommittees(): array + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + + $db->setQuery($db->getQuery(true) + ->select('c.id, c.name AS committee_name, c.description') + ->select('(SELECT COUNT(*) FROM #__mokosuitenpo_committee_members cm WHERE cm.committee_id = c.id AND cm.status = ' . $db->quote('active') . ') AS member_count') + ->select('(SELECT cd2.name FROM #__mokosuitenpo_committee_members cm2' + . ' JOIN #__contact_details cd2 ON cd2.id = cm2.contact_id' + . ' WHERE cm2.committee_id = c.id AND cm2.role = ' . $db->quote('chair') + . ' AND cm2.status = ' . $db->quote('active') . ' LIMIT 1) AS chair_name') + ->from($db->quoteName('#__mokosuitenpo_committees', 'c')) + ->where($db->quoteName('c.status') . ' = ' . $db->quote('active')) + ->order('c.name ASC')); + + return $db->loadObjectList() ?: []; + } + + /** + * Get meeting attendance rate for a board member. + */ + public static function getAttendanceRate(int $contactId, int $months = 12): object + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + $since = date('Y-m-d', strtotime("-{$months} months")); + + $db->setQuery($db->getQuery(true) + ->select('COUNT(*) AS total_meetings') + ->select('SUM(CASE WHEN ma.status = ' . $db->quote('present') . ' THEN 1 ELSE 0 END) AS attended') + ->select('SUM(CASE WHEN ma.status = ' . $db->quote('absent') . ' THEN 1 ELSE 0 END) AS absent') + ->select('SUM(CASE WHEN ma.status = ' . $db->quote('excused') . ' THEN 1 ELSE 0 END) AS excused') + ->from($db->quoteName('#__mokosuitenpo_meeting_attendance', 'ma')) + ->join('INNER', $db->quoteName('#__mokosuitenpo_meetings', 'm') . ' ON m.id = ma.meeting_id') + ->where('ma.contact_id = ' . (int) $contactId) + ->where('m.meeting_date >= ' . $db->quote($since))); + + $stats = $db->loadObject(); + $total = (int) ($stats->total_meetings ?? 0); + + return (object) [ + 'contact_id' => $contactId, + 'total_meetings' => $total, + 'attended' => (int) ($stats->attended ?? 0), + 'absent' => (int) ($stats->absent ?? 0), + 'excused' => (int) ($stats->excused ?? 0), + 'attendance_pct' => $total > 0 ? round((int) $stats->attended / $total * 100, 1) : 0, + ]; + } + + /** + * Get terms expiring within N days. + */ + public static function getExpiringTerms(int $days = 90): array + { + $db = Factory::getContainer()->get(DatabaseInterface::class); + $cutoff = date('Y-m-d', strtotime("+{$days} days")); + + $db->setQuery($db->getQuery(true) + ->select('bm.id, bm.role, bm.term_start, bm.term_end') + ->select('cd.name, cd.email_to') + ->from($db->quoteName('#__mokosuitenpo_board_members', 'bm')) + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = bm.contact_id') + ->where($db->quoteName('bm.status') . ' = ' . $db->quote('active')) + ->where('bm.term_end BETWEEN ' . $db->quote(date('Y-m-d')) . ' AND ' . $db->quote($cutoff)) + ->order('bm.term_end ASC')); + + return $db->loadObjectList() ?: []; + } +} -- 2.52.0 From 8f7f8c5fa6dabb3b780ebd00c8c8a3922f4c6bf9 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 21 Jun 2026 15:54:58 +0000 Subject: [PATCH 2/4] chore(version): auto-bump patch 01.06.01-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- source/packages/com_mokosuitenpo/mokosuitenpo.xml | 2 +- source/pkg_mokosuitenpo.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 79f6dd3..03bc2ef 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.06.00 +# VERSION: 01.06.01 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/source/packages/com_mokosuitenpo/mokosuitenpo.xml b/source/packages/com_mokosuitenpo/mokosuitenpo.xml index 6bc861e..1eb2e0f 100644 --- a/source/packages/com_mokosuitenpo/mokosuitenpo.xml +++ b/source/packages/com_mokosuitenpo/mokosuitenpo.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 01.06.00 + 01.06.01 8.3 MokoSuite NPO component Moko\Component\MokoSuiteNpo diff --git a/source/pkg_mokosuitenpo.xml b/source/pkg_mokosuitenpo.xml index f6fc2bf..e1ae3c3 100644 --- a/source/pkg_mokosuitenpo.xml +++ b/source/pkg_mokosuitenpo.xml @@ -2,7 +2,7 @@ Package - MokoSuite NPO mokosuitenpo - 01.06.00 + 01.06.01 2026-06-11 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From a8b202108bcbc1f5a9ca5d6cf25c57061a49346c Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 21 Jun 2026 15:55:06 +0000 Subject: [PATCH 3/4] chore(version): pre-release bump to 01.06.02-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- source/packages/com_mokosuitenpo/mokosuitenpo.xml | 2 +- source/pkg_mokosuitenpo.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 03bc2ef..3c2e9d4 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.06.01 +# VERSION: 01.06.02 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/source/packages/com_mokosuitenpo/mokosuitenpo.xml b/source/packages/com_mokosuitenpo/mokosuitenpo.xml index 1eb2e0f..5300382 100644 --- a/source/packages/com_mokosuitenpo/mokosuitenpo.xml +++ b/source/packages/com_mokosuitenpo/mokosuitenpo.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 01.06.01 + 01.06.02 8.3 MokoSuite NPO component Moko\Component\MokoSuiteNpo diff --git a/source/pkg_mokosuitenpo.xml b/source/pkg_mokosuitenpo.xml index e1ae3c3..add91bc 100644 --- a/source/pkg_mokosuitenpo.xml +++ b/source/pkg_mokosuitenpo.xml @@ -2,7 +2,7 @@ Package - MokoSuite NPO mokosuitenpo - 01.06.01 + 01.06.02 2026-06-11 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 8befe3a64aabe01970522964d130abd2b748dacd Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sun, 21 Jun 2026 15:01:33 +0000 Subject: [PATCH 4/4] chore: sync issue-branch.yml from Template-Generic [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 3c2e9d4..75a6963 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.06.02 +# VERSION: 01.00.00 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" -- 2.52.0