From b0983603e53a553bec4c25addc1fc1c51a3ae863 Mon Sep 17 00:00:00 2001 From: Rich Lott / Artful Robot Date: Thu, 20 Feb 2025 19:38:42 +0000 Subject: [PATCH] Main edit interface functinonal except save and execution order --- ang/crmContactcats.css | 38 ++++- ang/crmContactcats.js | 98 ++++++++++++ .../crmContactCategorySettings.html | 144 +++++++++++++++--- managed/CustomGroup_Category_changes.mgd.php | 83 ++++++++++ ...ionValue_changed_contact_category.mgd.php} | 4 +- managed/options.mgd.php | 51 ------- 6 files changed, 346 insertions(+), 72 deletions(-) create mode 100644 managed/CustomGroup_Category_changes.mgd.php rename managed/{activity-type-changed-cc.mgd.php => OptionValue_changed_contact_category.mgd.php} (80%) delete mode 100644 managed/options.mgd.php diff --git a/ang/crmContactcats.css b/ang/crmContactcats.css index 4d043bb..7c1b891 100644 --- a/ang/crmContactcats.css +++ b/ang/crmContactcats.css @@ -2,10 +2,44 @@ crm-contact-category-settings ol { padding: 0; } -crm-contact-category-settings ol li { +crm-contact-category-settings li.deleted { + opacity: 0.3; +} +/* @keyframes crm-contact-cats-move { */ +/* 0%, 90% {transform: none;} */ +/* 95% {transform: translateX(0.2em);} */ +/* 100% {transform: none;} */ +/* } */ + +crm-contact-category-settings .crm-contact-cats-move-target { + transition: opacity 0.3s ease, transform 0.3s ease, display 0.001s allow-discrete, max-height 0.3s ease; + transform-origin: top left; + display: none; +} +crm-contact-category-settings .moving .crm-contact-cats-move-target { + display: block; + transform: none; + opacity: 1; + max-height: 5rem; + @starting-style { + transform: scaleY(0); + opacity:0; + max-height: 0; + } +} + +crm-contact-category-settings li.being-moved .panel { + opacity: 0.8; +} +crm-contact-category-settings .panel { + margin-bottom: 0; +} +crm-contact-category-settings .panel-body { display: flex; + box-sizing: border-box; padding: 0.5em 0; - align-items: flex-end; + align-items: center; + justify-content: space-between; gap: 1em; } crm-contact-category-settings li label { diff --git a/ang/crmContactcats.js b/ang/crmContactcats.js index 0405af0..3d7a18f 100644 --- a/ang/crmContactcats.js +++ b/ang/crmContactcats.js @@ -22,11 +22,109 @@ // this.$onInit gets run after the this controller is called, and after the bindings have been applied. this.$onInit = async function() { ctrl.saved = false; + ctrl.view = 'list'; + ctrl.moveIdx = null; + ctrl.categoryToEdit = null; ctrl.categoryDefinitions = null; ctrl.categoryDefinitions = await crmApi4("ContactCategoryDefinition", 'get', { orderBy: { label: 'ASC' }, withLabels: true }); $scope.$digest(); + ctrl.edit = idx => { + if (idx === -1) { + // New item. + + // Create a random dark colour + let [r, g, b] = [Math.random(), Math.random(), Math.random()], + min = Math.min(r, g, b), + range = Math.max(r, g, b) - min, + conv = (x) => ('0' + Math.ceil((x-min)/range * 128).toString(16)).replace(/^.*(..)$/, '$1'); + + // Create a blank + ctrl.categoryToEdit = { + idx: ctrl.categoryDefinitions.length, + label: '', + search_type: 'search', + search_data: { saved_search_id: null }, + color: '#' + [r, g, b].map(conv).join(''), + icon: '', + description: '', + }; + } + else { + ctrl.categoryToEdit = Object.assign({idx}, JSON.parse(JSON.stringify(ctrl.categoryDefinitions[idx]))); + } + ctrl.view = 'edit'; + }; + + ctrl.moveTo = idx => { + let item = ctrl.categoryDefinitions.splice(ctrl.moveIdx, 1)[0]; + if (idx > ctrl.moveIdx) { + idx--; + } + ctrl.categoryDefinitions.splice(idx, 0, item); + ctrl.moveIdx = null; + }; + ctrl.deleteCategory = idx => { + if (!confirm(ts( + 'Confirm deleting category ‘%1’. You will lose history related to this category. Sure?', + {1: ctrl.categoryDefinitions[idx].label} + ))) { + return; + } + ctrl.categoryDefinitions[idx].deleted = true; + ctrl.categoryToEdit = null; + ctrl.view = 'list'; + }; + + ctrl.updateEditedThing = () => { + // @todo validate, e.g. + if (!ctrl.categoryToEdit.label) { + alert("No name"); + return; + } + + const search_data = ctrl.categoryToEdit.search_data; + console.log("search_data", search_data); + if (ctrl.categoryToEdit.search_type === 'group') { + // Only store what we need. + const {group_id} = search_data; + console.log("group_id", group_id, search_data); + ctrl.categoryToEdit.search_data = {group_id}; + } + else if (ctrl.categoryToEdit.search_type === 'search') { + const {saved_search_id} = search_data; + ctrl.categoryToEdit.search_data = {saved_search_id}; + } + + const edited = ctrl.categoryToEdit; + const idx = edited.idx; + delete(edited.idx); + ctrl.categoryDefinitions[idx] = edited; + ctrl.categoryToEdit = null; + ctrl.view = 'list'; + console.log("done editing"); + } + + // We need to ensure the search_data object contains the fields required for the selected search_type + ctrl.fixSearchData = () => { + const search_data = ctrl.categoryToEdit.search_data; + if (ctrl.categoryToEdit.search_type === 'group') { + if (!('group_id' in search_data)) { + search_data.group_id = null; + } + } + else if (ctrl.categoryToEdit.search_type === 'search') { + if (!('saved_search_id' in search_data)) { + search_data.saved_search_id = null; + } + } + }; + + ctrl.save = async () => { + if (!confirm(ts("Confirm saving changes to categories? Note that categories will not be fully applied until tomorrow."))) { return; } + }; + // ctrl.deleteRow = idx => { // ctrl.catmap.splice(idx, 1); // }; diff --git a/ang/crmContactcats/crmContactCategorySettings.html b/ang/crmContactcats/crmContactCategorySettings.html index 70cc933..24f9647 100644 --- a/ang/crmContactcats/crmContactCategorySettings.html +++ b/ang/crmContactcats/crmContactCategorySettings.html @@ -1,11 +1,9 @@
Loading...
-
- - + -
    -
  1. +
      +
    1. + +
      + +
      +
      +
      + + + {{row.label}} + - - - {{row.label}} - - - - - + + + + + + + + +
      +
      + +
      + +
    +

    + +

    + + +

    +
    Categories saved. Contacts will be updated shortly (when the next Scheduled Job run happens).
  2. + + +
    +
    +

    {{ts('Edit category %1', {1 : $ctrl.categoryDefinitions[$ctrl.categoryToEdit.idx].label })}}

    +

    {{ts('Add new category')}}

    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    + +
    + + + + + + +
    +
    + + diff --git a/managed/CustomGroup_Category_changes.mgd.php b/managed/CustomGroup_Category_changes.mgd.php new file mode 100644 index 0000000..d0aeb05 --- /dev/null +++ b/managed/CustomGroup_Category_changes.mgd.php @@ -0,0 +1,83 @@ + 'CustomGroup_Category_changes', + 'entity' => 'CustomGroup', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'name' => 'Category_changes', + 'title' => E::ts('Category changes'), + 'extends' => 'Activity', + 'extends_entity_column_value:name' => ['changed_contact_category'], + 'style' => 'Inline', + 'help_pre' => E::ts(''), + 'help_post' => E::ts(''), + 'weight' => 18, + 'collapse_adv_display' => TRUE, + 'created_date' => '2025-02-20 13:59:06', + 'is_public' => FALSE, + 'icon' => '', + ], + 'match' => ['name'], + ], + ], + [ + 'name' => 'CustomGroup_Category_changes_CustomField_Previous_Category', + 'entity' => 'CustomField', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'custom_group_id.name' => 'Category_changes', + 'name' => 'Previous_Category', + 'label' => E::ts('Previous Category'), + 'data_type' => 'EntityReference', + 'html_type' => 'Autocomplete-Select', + 'is_searchable' => TRUE, + 'is_view' => TRUE, + 'text_length' => 255, + 'note_columns' => 60, + 'note_rows' => 4, + 'column_name' => 'previous_category_117', + 'fk_entity' => 'ContactCategoryDefinition', + ], + 'match' => [ + 'name', + 'custom_group_id', + ], + ], + ], + [ + 'name' => 'CustomGroup_Category_changes_CustomField_New_Category', + 'entity' => 'CustomField', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'custom_group_id.name' => 'Category_changes', + 'name' => 'New_Category', + 'label' => E::ts('New Category'), + 'data_type' => 'EntityReference', + 'html_type' => 'Autocomplete-Select', + 'is_searchable' => TRUE, + 'is_view' => TRUE, + 'text_length' => 255, + 'note_columns' => 60, + 'note_rows' => 4, + 'column_name' => 'new_category_118', + 'fk_entity' => 'ContactCategoryDefinition', + ], + 'match' => [ + 'name', + 'custom_group_id', + ], + ], + ], +]; diff --git a/managed/activity-type-changed-cc.mgd.php b/managed/OptionValue_changed_contact_category.mgd.php similarity index 80% rename from managed/activity-type-changed-cc.mgd.php rename to managed/OptionValue_changed_contact_category.mgd.php index 4e9ed7c..1be1df0 100644 --- a/managed/activity-type-changed-cc.mgd.php +++ b/managed/OptionValue_changed_contact_category.mgd.php @@ -3,14 +3,14 @@ use CRM_Contactcats_ExtensionUtil as E; return [ [ - 'name' => 'OptionValue_Changed_Contact_Category', + 'name' => 'OptionValue_changed_contact_category', 'entity' => 'OptionValue', 'cleanup' => 'unused', 'update' => 'unmodified', 'params' => [ 'version' => 4, 'values' => [ - 'option_group_id:name' => 'activity_type', + 'option_group_id.name' => 'activity_type', 'label' => E::ts('Changed Contact Category'), 'name' => 'changed_contact_category', 'icon' => 'fa-tags', diff --git a/managed/options.mgd.php b/managed/options.mgd.php deleted file mode 100644 index 665d87d..0000000 --- a/managed/options.mgd.php +++ /dev/null @@ -1,51 +0,0 @@ - 'OptionGroup_contact_categories', - 'entity' => 'OptionGroup', - 'cleanup' => 'unused', - 'update' => 'unmodified', - 'params' => [ - 'version' => 4, - 'values' => [ - 'name' => 'contact_categories', - 'title' => E::ts('Contact Categories'), - 'data_type' => 'Integer', - 'is_reserved' => FALSE, - 'is_active' => TRUE, - 'option_value_fields' => [ - 'name', - 'label', - 'description', - 'weight', - ], - ], - 'match' => [ - 'name', - ], - ], - ], - [ - 'name' => 'OptionGroup_contact_categories_OptionValue_Default', - 'entity' => 'OptionValue', - 'cleanup' => 'unused', - 'update' => 'unmodified', - 'params' => [ - 'version' => 4, - 'values' => [ - 'option_group_id.name' => 'contact_categories', - 'label' => E::ts('Default'), - 'is_active' => TRUE, - 'value' => '0', - 'name' => 'default', - ], - 'match' => [ - 'option_group_id', - 'name', - 'value', - ], - ], - ], -];