diff --git a/dt-groups/base-setup.php b/dt-groups/base-setup.php
index ab16189b7..d70a528cc 100644
--- a/dt-groups/base-setup.php
+++ b/dt-groups/base-setup.php
@@ -1196,6 +1196,48 @@ public function scripts(){
}
}
+ // Filter fields that should be shown in create form
+ // Note: title field is handled separately in JavaScript, so we exclude it here
+ $create_form_fields = [];
+ foreach ( $field_settings as $field_key => $field_setting ) {
+ // Skip title field - it's rendered separately in JavaScript
+ if ( $field_key === 'title' ) {
+ continue;
+ }
+
+ // Skip user_select (e.g. assigned_to) - not part of Add Child flow; uses legacy typeahead, not collected
+ if ( isset( $field_setting['type'] ) && $field_setting['type'] === 'user_select' ) {
+ continue;
+ }
+
+ // Skip hidden fields unless they have custom_display
+ if ( !empty( $field_setting['hidden'] ) && empty( $field_setting['custom_display'] ) ) {
+ continue;
+ }
+ // Skip fields explicitly set to not show in create form
+ if ( isset( $field_setting['in_create_form'] ) && $field_setting['in_create_form'] === false ) {
+ continue;
+ }
+ // Include fields that have in_create_form => true or list this post type in the array
+ if ( !empty( $field_setting['in_create_form'] ) ) {
+ $in_form = $field_setting['in_create_form'];
+ if ( $in_form === true ) {
+ $create_form_fields[ $field_key ] = $field_setting;
+ } elseif ( is_array( $in_form ) && in_array( $this->post_type, $in_form, true ) ) {
+ $create_form_fields[ $field_key ] = $field_setting;
+ }
+ }
+ }
+
+ // Title field is rendered separately in JS; pass its settings as a dedicated key (not in fieldSettings loop).
+ $title_field_setting = isset( $field_settings['title'] ) ? $field_settings['title'] : null;
+
+ // Get mapbox token for location fields
+ $mapbox_key = '';
+ if ( class_exists( 'DT_Mapbox_API' ) ) {
+ $mapbox_key = DT_Mapbox_API::get_key() ?? '';
+ }
+
wp_localize_script( $genmap_script_handle, 'dtGroupGenmap', [
'statusField' => [
'key' => $status_key,
@@ -1204,18 +1246,32 @@ public function scripts(){
],
'groupTypes' => $group_type_labels,
'groupTypeIcons' => $group_type_icons,
+ 'fieldSettings' => $create_form_fields,
+ 'titleFieldSetting' => $title_field_setting,
+ 'mapboxKey' => $mapbox_key,
'strings' => [
'loading' => __( 'Loading map…', 'disciple_tools' ),
'error' => __( 'Unable to load generational map.', 'disciple_tools' ),
'empty' => __( 'No child groups to display.', 'disciple_tools' ),
+ 'chart_aria' => __( 'Group generational map', 'disciple_tools' ),
'details' => [
'open' => __( 'Open', 'disciple_tools' ),
'add' => __( 'Add', 'disciple_tools' ),
+ 'close' => __( 'Close', 'disciple_tools' ),
+ 'type' => __( 'Type', 'disciple_tools' ),
+ 'status' => __( 'Status', 'disciple_tools' ),
+ 'members' => __( 'Members', 'disciple_tools' ),
+ 'assigned' => __( 'Assigned', 'disciple_tools' ),
+ 'expand' => __( 'Expand', 'disciple_tools' ),
+ 'collapse' => __( 'Collapse', 'disciple_tools' ),
],
'modal' => [
'add_child_title' => __( 'Add Child To', 'disciple_tools' ),
'add_child_name_title' => __( 'Name', 'disciple_tools' ),
'add_child_but' => __( 'Add Child', 'disciple_tools' ),
+ 'name_required' => __( 'Name is required', 'disciple_tools' ),
+ 'error_creating_child' => __( 'Error creating child group: %s', 'disciple_tools' ),
+ 'unknown_error' => __( 'Unknown error', 'disciple_tools' ),
],
],
'recordUrlBase' => trailingslashit( site_url() ),
diff --git a/dt-groups/genmap-d3.css b/dt-groups/genmap-d3.css
index da5d10ad6..2f00d7469 100644
--- a/dt-groups/genmap-d3.css
+++ b/dt-groups/genmap-d3.css
@@ -396,3 +396,12 @@
outline: 2px solid #eed936;
outline-offset: 2px;
}
+
+/* Add Child modal (Genmapper): allow location dropdown to extend outside (avoid clipping) */
+#template_metrics_modal.genmap-add-child-modal {
+ overflow: visible;
+}
+
+#template_metrics_modal.genmap-add-child-modal #template_metrics_modal_content .form-field-location {
+ overflow: visible;
+}
diff --git a/dt-groups/genmap-tile.js b/dt-groups/genmap-tile.js
index 7d2e3359e..8f3e2f884 100644
--- a/dt-groups/genmap-tile.js
+++ b/dt-groups/genmap-tile.js
@@ -6,6 +6,7 @@
const MAX_CANVAS_HEIGHT = 320;
const MIN_CANVAS_HEIGHT = 220;
const LAYOUT_STORAGE_KEY = 'group_genmap_layout';
+ const GENMAP_ADD_CHILD_FIELD_PREFIX = 'group_genmap_add_child_';
// Node dimensions constants
const NODE_WIDTH = 72; // Increased from 60px to prevent text clipping
@@ -658,6 +659,7 @@
* @returns {string} HTML content
*/
function buildPopoverContent(nodeData) {
+ // Generation number is intentionally not shown in the popover (see issue #2878).
const data = nodeData.data;
const strings = window.dtGroupGenmap?.strings || {};
const detailsStrings = strings.details || {};
@@ -677,13 +679,13 @@
// Build HTML with header containing title and close button
let html = '
';
// Group type with icon
if (groupTypeLabel) {
html += '';
- html += '
Type:';
+ html += `
${window.lodash.escape(detailsStrings.type || 'Type')}:`;
if (groupTypeIcon) {
html += `
${window.lodash.escape(groupTypeLabel)}`;
} else {
@@ -696,19 +698,11 @@
if (status) {
const statusColor = data.statusColor || '#3f729b';
html += '
';
- html += 'Status:';
+ html += `${window.lodash.escape(detailsStrings.status || 'Status')}:`;
html += `${window.lodash.escape(status)}`;
html += '
';
}
- // Generation
- if (data.content) {
- html += '
';
- html += 'Generation:';
- html += `${window.lodash.escape(data.content)}`;
- html += '
';
- }
-
// Actions
html += '
';
@@ -725,8 +719,10 @@
(nodeData._children && nodeData._children.length > 0)
) {
const isCollapsed = nodeData.data.collapsed || false;
- const collapseText = isCollapsed ? 'Expand' : 'Collapse';
- html += ``;
+ const collapseText = isCollapsed
+ ? detailsStrings.expand || 'Expand'
+ : detailsStrings.collapse || 'Collapse';
+ html += ``;
}
html += '
';
@@ -1993,7 +1989,7 @@
style="width: 100%; height: 100%;">
`;
@@ -2103,27 +2099,36 @@
let detailsHtml = '';
detailsHtml += '
';
+ const detailsLabels = window.dtGroupGenmap?.strings?.details || {};
if (data.group_status && data.group_status.label) {
detailsHtml +=
- '
Status: ' +
+ '
' +
+ window.lodash.escape(detailsLabels.status || 'Status') +
+ ': ' +
window.lodash.escape(data.group_status.label) +
'
';
}
if (data.group_type && data.group_type.label) {
detailsHtml +=
- '
Type: ' +
+ '
' +
+ window.lodash.escape(detailsLabels.type || 'Type') +
+ ': ' +
window.lodash.escape(data.group_type.label) +
'
';
}
if (data.member_count !== undefined) {
detailsHtml +=
- '
Members: ' +
+ '
' +
+ window.lodash.escape(detailsLabels.members || 'Members') +
+ ': ' +
window.lodash.escape(data.member_count) +
'
';
}
if (data.assigned_to && data.assigned_to.display) {
detailsHtml +=
- '
Assigned: ' +
+ '
' +
+ window.lodash.escape(detailsLabels.assigned || 'Assigned') +
+ ': ' +
window.lodash.escape(data.assigned_to.display) +
'
';
}
@@ -2177,19 +2182,95 @@
}
const modalStrings = window.dtGroupGenmap?.strings?.modal || {};
- const listHtml = `
-
-
-
`;
+ )}" />`;
+
+ // fieldSettings contains only in_create_form fields (title excluded in PHP). Title is rendered from titleFieldSetting.
+ const fieldsToRender = {};
+ Object.keys(fieldSettings).forEach((key) => {
+ // Skip title (handled separately) and legacy "name" field if present to avoid duplicating the label.
+ if (key !== 'title' && key !== 'name') {
+ fieldsToRender[key] = fieldSettings[key];
+ }
+ });
+
+ // Title field is passed as a separate top-level key from PHP (titleFieldSetting)
+ const titleFieldSetting = window.dtGroupGenmap?.titleFieldSetting || {
+ name: modalStrings.add_child_name_title || 'Name',
+ type: 'text',
+ };
+
+ if (window.SHAREDFUNCTIONS && window.SHAREDFUNCTIONS.renderField) {
+ const titleFieldHtml = window.SHAREDFUNCTIONS.renderField(
+ 'title',
+ titleFieldSetting,
+ fieldPrefix,
+ );
+ if (titleFieldHtml) {
+ listHtml += `
${titleFieldHtml}
`;
+ } else {
+ // Fallback if renderField returns null
+ listHtml += `
+
`;
+ }
+ } else {
+ // Fallback if web components not available
+ listHtml += `
+
`;
+ }
+
+ // Render other fields that have in_create_form => true
+ const hasRenderField = !!(
+ window.SHAREDFUNCTIONS && window.SHAREDFUNCTIONS.renderField
+ );
+ if (Object.keys(fieldsToRender).length > 0 && !hasRenderField) {
+ console.warn(
+ 'Genmapper Add Child: SHAREDFUNCTIONS.renderField is unavailable; in_create_form fields other than title will not be rendered.',
+ );
+ }
+ Object.keys(fieldsToRender).forEach((fieldKey) => {
+ // Safety guard in case title/name ever slip into fieldsToRender.
+ if (fieldKey === 'title' || fieldKey === 'name') {
+ return;
+ }
+
+ const fieldSetting = fieldsToRender[fieldKey];
+ // Only render fields that are supported by renderField
+ if (hasRenderField && fieldSetting && fieldSetting.type) {
+ const fieldHtml = window.SHAREDFUNCTIONS.renderField(
+ fieldKey,
+ fieldSetting,
+ fieldPrefix,
+ );
- const buttonsHtml = `