From 3e8d3d1de67f15328e23d833b42ddaac6feb2b5f Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Thu, 20 May 2021 19:01:50 -0700 Subject: [PATCH 01/13] add title hint for main attendee in familyattendee datagrid, and fix the bug of saving contacts --- attendees/static/js/persons/datagrid_attendee_update_view.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index ed553a12..0cf60e51 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -516,7 +516,7 @@ Attendees.datagridUpdate = { const userData = new FormData($('form#attendee-update-form')[0]); if(!$('input[name="photo"]')[0].value){userData.delete("photo")}; - + userData.set('infos', JSON.stringify(Attendees.datagridUpdate.attendeeFormConfigs.formData.infos)); userData._method = userData.id ? 'PUT' : 'POST'; $.ajax({ @@ -1502,6 +1502,7 @@ Attendees.datagridUpdate = { onRowPrepared: (e) => { if (e.rowType === 'data' && e.data.attendee && e.data.attendee.id === Attendees.datagridUpdate.attendeeId) { e.rowElement.css("color", "SeaGreen"); + e.rowElement.attr('title', "Please scroll up and change main attendee data there!"); } }, columns:[ From 9191a9bfe604f9c2aa2db01f5df75d5b1b109a5d Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Thu, 20 May 2021 19:09:19 -0700 Subject: [PATCH 02/13] first test of removing columns --- .../persons/datagrid_attendee_update_view.js | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 0cf60e51..370f63c1 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -197,25 +197,28 @@ Attendees.datagridUpdate = { }, { colSpan: 20, - colCount: 20, + colCount: 21, itemType: "group", caption: "Basic info. Fields after nick name can be removed by clearing & save.", // adding element in caption by $("", {text:"hi 5"}).appendTo($("span.dx-form-group-caption")[1]) items: [ - { - colSpan: 7, -// caption: "colSpan: 7", - itemType: "group", - items: [ +// { +// colSpan: 7, +// // caption: "colSpan: 7", +// itemType: "group", +// items: [ { + colSpan: 7, dataField: "first_name", editorOptions: { placeholder: "English", }, }, { + colSpan: 7, dataField: "last_name2", }, { + colSpan: 7, dataField: "actual_birthday", editorType: "dxDateBox", editorOptions: { @@ -226,6 +229,7 @@ Attendees.datagridUpdate = { }, }, { + colSpan: 7, dataField: "infos.contacts.phone1", label: { text: 'phone1', @@ -233,28 +237,32 @@ Attendees.datagridUpdate = { // editorOptions: {mask: "+1 (X00) 000-0000",} }, { + colSpan: 7, dataField: "infos.contacts.email1", label: { text: 'email1', }, }, - ], - }, - { - colSpan: 7, -// caption: "colSpan: 6", - itemType: "group", - items: [ +// ], +// }, +// { +// colSpan: 7, +// // caption: "colSpan: 6", +// itemType: "group", +// items: [ { + colSpan: 7, dataField: "last_name", editorOptions: { placeholder: "English", }, }, { + colSpan: 7, dataField: "first_name2", }, { + colSpan: 7, dataField: "estimated_birthday", editorType: "dxDateBox", editorOptions: { @@ -265,6 +273,7 @@ Attendees.datagridUpdate = { }, }, { + colSpan: 7, dataField: "infos.contacts.phone2", label: { text: 'phone2', @@ -272,18 +281,20 @@ Attendees.datagridUpdate = { // editorOptions: {mask: "+1 (X00) 000-0000",} }, { + colSpan: 7, dataField: "infos.contacts.email2", label: { text: 'email2', }, }, - ], - }, - { - colSpan: 6, - itemType: "group", - items: [ + // ], + // }, + // { + // colSpan: 6, + // itemType: "group", + // items: [ { + colSpan: 7, dataField: "division", editorType: "dxSelectBox", isRequired: true, @@ -310,6 +321,7 @@ Attendees.datagridUpdate = { }, }, { + colSpan: 7, dataField: "gender", editorType: "dxSelectBox", isRequired: true, @@ -326,6 +338,7 @@ Attendees.datagridUpdate = { ], }, { + colSpan: 7, dataField: "deathday", editorType: "dxDateBox", editorOptions: { @@ -333,13 +346,14 @@ Attendees.datagridUpdate = { }, }, { + colSpan: 7, dataField: "infos.contacts.nick_name", label: { text: 'nick name', }, }, - ], - }, + // ], + // }, ], }, { From 94ac92b4424618564a4b22d060c604489851ae67 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Thu, 20 May 2021 19:18:52 -0700 Subject: [PATCH 03/13] rearrange finished --- .../persons/datagrid_attendee_update_view.js | 284 +++++++++--------- 1 file changed, 135 insertions(+), 149 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 370f63c1..191f6874 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -201,159 +201,145 @@ Attendees.datagridUpdate = { itemType: "group", caption: "Basic info. Fields after nick name can be removed by clearing & save.", // adding element in caption by $("", {text:"hi 5"}).appendTo($("span.dx-form-group-caption")[1]) items: [ -// { -// colSpan: 7, -// // caption: "colSpan: 7", -// itemType: "group", -// items: [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "last_name2", - }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, - }, -// ], -// }, -// { -// colSpan: 7, -// // caption: "colSpan: 6", -// itemType: "group", -// items: [ - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "first_name2", - }, - { - colSpan: 7, - dataField: "estimated_birthday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, - }, - // ], - // }, - // { - // colSpan: 6, - // itemType: "group", - // items: [ - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, - }, + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', + }, + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), + }, + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", + }, + validationRules: [ { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", - }, - validationRules: [ - { - type: "required", - message: "gender is required" - }, - ], + type: "required", + message: "gender is required" }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - }, + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', + }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, + }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', + }, + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', }, - // ], - // }, + }, + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, ], }, { From 6d9cf5efcc7d596754a3a0f8bff88032abefddc7 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 08:39:17 -0700 Subject: [PATCH 04/13] move basic info block to a new factory function for dynamically changes later --- .../persons/datagrid_attendee_update_view.js | 289 +++++++++--------- attendees/static/js/shared/utilities.js | 9 +- 2 files changed, 156 insertions(+), 142 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 191f6874..6e656456 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -116,6 +116,7 @@ Attendees.datagridUpdate = { $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); + Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", Attendees.datagridUpdate.attendeeFormBasicInfoItemsGenerator()); Attendees.datagridUpdate.initListeners(); }, error : (response) => { @@ -125,6 +126,151 @@ Attendees.datagridUpdate = { }, + attendeeFormBasicInfoItemsGenerator: () => { + const items = [ + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', + }, + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), + }, + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", + }, + validationRules: [ + { + type: "required", + message: "gender is required" + }, + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', + }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', + }, + }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', + }, + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', + }, + }, + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, + ]; + return items; + }, + attendeeFormConfigs: { readOnly: !Attendees.utilities.editingEnabled, onContentReady: () => { @@ -199,148 +345,9 @@ Attendees.datagridUpdate = { colSpan: 20, colCount: 21, itemType: "group", + name: "basic-info-container", caption: "Basic info. Fields after nick name can be removed by clearing & save.", // adding element in caption by $("", {text:"hi 5"}).appendTo($("span.dx-form-group-caption")[1]) - items: [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, - }, - { - colSpan: 7, - dataField: "last_name2", - }, - { - colSpan: 7, - dataField: "first_name2", - }, - { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", - }, - validationRules: [ - { - type: "required", - message: "gender is required" - }, - ], - }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - label: { - text: 'Real birthday', - }, - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, - }, - }, - { - colSpan: 7, - dataField: "estimated_birthday", - label: { - text: 'Guess birthday', - }, - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, - }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, - }, - ], + items: [], // will trigger later for dynamic items }, { colSpan: 24, diff --git a/attendees/static/js/shared/utilities.js b/attendees/static/js/shared/utilities.js index 695695cc..c307abc0 100644 --- a/attendees/static/js/shared/utilities.js +++ b/attendees/static/js/shared/utilities.js @@ -111,7 +111,14 @@ Attendees.utilities = { {name: 'UNSPECIFIED'}, ]; }, -} + + basicContacts: [ + 'phone1', + 'phone2', + 'email1', + 'email2' + ], +}; $(document).ready(() => { Attendees.utilities.init(); From ab604a64aac207b4344a5f4fbcad1d661d76267f Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 09:48:39 -0700 Subject: [PATCH 05/13] empty custom contact popup shows now --- .../persons/datagrid_attendee_update_view.js | 383 +++++++++++------- .../datagrid_attendee_update_view.html | 4 + 2 files changed, 245 insertions(+), 142 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 6e656456..cedf6ded 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -40,6 +40,7 @@ Attendees.datagridUpdate = { $("div.form-container").on("click", "button.attendingmeet-button", e => Attendees.datagridUpdate.initAttendingmeetPopupDxForm(e)); $("div.form-container").on("click", "button.attendee-place-button", e => Attendees.datagridUpdate.initPlacePopupDxForm(e)); $("div.form-container").on("click", "button.family-button", e => Attendees.datagridUpdate.initFamilyAttrPopupDxForm(e)); + Attendees.datagridUpdate.attachContactAddButton(); // add listeners for Family, counselling, etc. }, @@ -126,151 +127,24 @@ Attendees.datagridUpdate = { }, - attendeeFormBasicInfoItemsGenerator: () => { - const items = [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, - }, - { - colSpan: 7, - dataField: "last_name2", - }, - { - colSpan: 7, - dataField: "first_name2", - }, - { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", - }, - validationRules: [ - { - type: "required", - message: "gender is required" + attachContactAddButton: () => { + $("", {class: "extra-contacts"}) + .dxButton({ + text:'Add more contact', + icon:"email", + type:"normal", + height: '1.4rem', + hint: 'add more different contacts such as more phones/emails', + onClick: ()=> { + console.log('hi 139 initializing contactPopup ...'); + Attendees.datagridUpdate.contactPopup = $("div.popup-more-contacts").dxPopup(Attendees.datagridUpdate.contactPopupDxFormConfig()).dxPopup("instance"); }, - ], - }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - label: { - text: 'Real birthday', - }, - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, - }, - }, - { - colSpan: 7, - dataField: "estimated_birthday", - label: { - text: 'Guess birthday', - }, - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, - }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, - }, - ]; - return items; + }) + .appendTo($("span.dx-form-group-caption")[1]); }, + + attendeeFormConfigs: { readOnly: !Attendees.utilities.editingEnabled, onContentReady: () => { @@ -561,6 +435,231 @@ Attendees.datagridUpdate = { ] }, + attendeeFormBasicInfoItemsGenerator: () => { + const items = [ + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', + }, + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), + }, + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", + }, + validationRules: [ + { + type: "required", + message: "gender is required" + }, + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', + }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', + }, + }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', + }, + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', + }, + }, + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, + ]; + //Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts['email3']="hello world" + return items; + }, + + contactPopupDxFormConfig: () => { + return { + visible: true, + title: 'Add Contact', + minwidth: "20%", + minheight: "30%", + position: { + my: 'center', + at: 'center', + of: window, + }, + dragEnabled: true, + contentTemplate: (e) => { + const formContainer = $('
'); + Attendees.datagridUpdate.contactPopupDxForm = formContainer.dxForm({ + // disable: !Attendees.utilities.editingEnabled, + // formData: Attendees.datagridUpdate.placeDefaults, + scrollingEnabled: true, + showColonAfterLabel: false, + requiredMark: "*", + // labelLocation: "top", + // minColWidth: "20%", + showValidationSummary: true, + items: [ + { + dataField: "contactKey", + isRequired: true, + validationRules: [ + { + type: "required", + message: "required" + }, + ], + }, + { + dataField: "contactValue", + isRequired: true, + validationRules: [ + { + type: "required", + message: "required" + }, + ], + }, + { + itemType: "button", + buttonOptions: { + elementAttr: { + class: 'attendee-form-submits', // for toggling editing mode + }, + // disabled: !Attendees.utilities.editingEnabled, + text: "Save Custom Contact", + icon: "save", + hint: "save Custom Contact in the popup", + type: "default", + useSubmitBehavior: false, + onClick: (clickEvent) => { + const userData = {contact: 'hi 641'}; + console.log("hi 642"); + // $.ajax({ + // url : ajaxUrl, + // data : JSON.stringify(userData), + // dataType:'json', + // contentType: "application/json; charset=utf-8", + // method : 'PATCH', + // success: (response) => console.log('599 Success to save data for contact Form in Popup, response: ', response), + // error : (response) => console.log('600 Failed to save data for contact Form in Popup, response: ', response), + // }); + }, + }, + }, + + ], + }).dxForm("instance"); + e.append(formContainer); + }, + }; + }, + /////////////////////// Attending Meet Popup and DxForm /////////////////////// diff --git a/attendees/templates/persons/datagrid_attendee_update_view.html b/attendees/templates/persons/datagrid_attendee_update_view.html index 1d220761..1b0fe7c4 100644 --- a/attendees/templates/persons/datagrid_attendee_update_view.html +++ b/attendees/templates/persons/datagrid_attendee_update_view.html @@ -90,6 +90,10 @@

data-assembly="{{current_assembly_slug}}">

+
From 6dcbe3c94fdb0c75ca386c5367251162bf221fb1 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 14:36:11 -0700 Subject: [PATCH 06/13] UI now can add uniq custom contacts, the next is to ajax to server for update --- .../persons/datagrid_attendee_update_view.js | 325 ++++++++++-------- attendees/static/js/shared/utilities.js | 9 + 2 files changed, 196 insertions(+), 138 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index cedf6ded..1af1fd23 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -45,7 +45,7 @@ Attendees.datagridUpdate = { }, toggleEditing: (enabled) => { - $('div.attendee-form-submits').dxButton('instance').option('disabled', !enabled); + $('div.attendee-form-submits, span.attendee-form-submits').dxButton('instance').option('disabled', !enabled); $('button.attendingmeet-button-new, button.family-button-new, button.place-button-new, input.form-check-input').prop('disabled', !enabled); Attendees.datagridUpdate.attendeeMainDxForm.option("readOnly", !enabled); Attendees.datagridUpdate.attendeePhotoFileUploader.option("disabled", !enabled); @@ -117,7 +117,7 @@ Attendees.datagridUpdate = { $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); - Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", Attendees.datagridUpdate.attendeeFormBasicInfoItemsGenerator()); + Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", Attendees.datagridUpdate.attendeeFormBasicInfoItems); Attendees.datagridUpdate.initListeners(); }, error : (response) => { @@ -130,15 +130,18 @@ Attendees.datagridUpdate = { attachContactAddButton: () => { $("", {class: "extra-contacts"}) .dxButton({ + disabled: !Attendees.utilities.editingEnabled, + elementAttr: { + class: 'attendee-form-submits', // for toggling editing mode + }, text:'Add more contact', icon:"email", type:"normal", height: '1.4rem', hint: 'add more different contacts such as more phones/emails', onClick: ()=> { - console.log('hi 139 initializing contactPopup ...'); Attendees.datagridUpdate.contactPopup = $("div.popup-more-contacts").dxPopup(Attendees.datagridUpdate.contactPopupDxFormConfig()).dxPopup("instance"); - }, + }, }) .appendTo($("span.dx-form-group-caption")[1]); }, @@ -435,151 +438,147 @@ Attendees.datagridUpdate = { ] }, - attendeeFormBasicInfoItemsGenerator: () => { - const items = [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, + attendeeFormBasicInfoItems: [ + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", }, - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", }, - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', }, - { - colSpan: 7, - dataField: "last_name2", + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), }, - { - colSpan: 7, - dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", }, - { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", + validationRules: [ + { + type: "required", + message: "gender is required" }, - validationRules: [ - { - type: "required", - message: "gender is required" - }, - ], + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - label: { - text: 'Real birthday', - }, - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', }, }, - { - colSpan: 7, - dataField: "estimated_birthday", - label: { - text: 'Guess birthday', - }, - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', }, }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', }, - ]; - //Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts['email3']="hello world" - return items; - }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, + ], contactPopupDxFormConfig: () => { return { @@ -596,8 +595,9 @@ Attendees.datagridUpdate = { contentTemplate: (e) => { const formContainer = $('
'); Attendees.datagridUpdate.contactPopupDxForm = formContainer.dxForm({ - // disable: !Attendees.utilities.editingEnabled, + // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), // formData: Attendees.datagridUpdate.placeDefaults, + // onFieldDataChanged: (e) => {e.component.validate()}, scrollingEnabled: true, showColonAfterLabel: false, requiredMark: "*", @@ -607,39 +607,87 @@ Attendees.datagridUpdate = { items: [ { dataField: "contactKey", + editorOptions: { + placeholder: 'for example: WeChat1', + }, + helpText: 'Any contact such as email3/phone3/fax1, etc', + label: { + text: 'Contact method', + }, isRequired: true, validationRules: [ { type: "required", - message: "required" + message: "Contact method is required" }, + { + type: "stringLength", + min: 2, + message: "Contact method can't be less than 2 characters" + }, + { + type: "custom", + message: "That contact method exists already", + validationCallback: (e) => { + const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + return !Object.keys(currentContacts).includes(e.value.trim()); + } + } ], }, { dataField: "contactValue", + editorOptions: { + placeholder: 'for example: WeiXin', + }, + helpText: 'Contact such as name@gmail.com/+15101234567 etc', + label: { + text: 'Contact content', + }, isRequired: true, validationRules: [ { type: "required", - message: "required" + message: "Contact content is required" + }, + { + type: "stringLength", + min: 2, + message: "Contact content can't be less than 2 characters" }, ], }, { itemType: "button", + // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), buttonOptions: { elementAttr: { class: 'attendee-form-submits', // for toggling editing mode }, - // disabled: !Attendees.utilities.editingEnabled, + disabled: !Attendees.utilities.editingEnabled, text: "Save Custom Contact", icon: "save", hint: "save Custom Contact in the popup", type: "default", useSubmitBehavior: false, onClick: (clickEvent) => { - const userData = {contact: 'hi 641'}; - console.log("hi 642"); + if (Attendees.datagridUpdate.contactPopupDxForm.validate().isValid){ + const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + const newContact = Attendees.datagridUpdate.contactPopupDxForm.option('formData'); + const trimmedContact = Attendees.utilities.trimBothKeyAndValue(newContact); + + currentContacts[trimmedContact.contactKey]=trimmedContact.contactValue; + newBasicInfoItems = Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container").items.concat({ + colSpan: 7, + dataField: "infos.contacts." + trimmedContact.contactKey, + label: { + text: trimmedContact.contactKey, + }, + }); + + Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", newBasicInfoItems); + Attendees.datagridUpdate.contactPopup.hide(); + // $.ajax({ // url : ajaxUrl, // data : JSON.stringify(userData), @@ -649,6 +697,7 @@ Attendees.datagridUpdate = { // success: (response) => console.log('599 Success to save data for contact Form in Popup, response: ', response), // error : (response) => console.log('600 Failed to save data for contact Form in Popup, response: ', response), // }); + } }, }, }, diff --git a/attendees/static/js/shared/utilities.js b/attendees/static/js/shared/utilities.js index c307abc0..5897f91f 100644 --- a/attendees/static/js/shared/utilities.js +++ b/attendees/static/js/shared/utilities.js @@ -44,6 +44,15 @@ Attendees.utilities = { }); }, // jQuery toggle() from https://supportcenter.devexpress.com/ticket/details/t525231 + trimBothKeyAndValue: (obj) => { + return Object.entries(obj).reduce((acc, curr) => { + const [key, value] = curr; + acc[key.trim()] = value.trim(); + return acc + }, {}); + }, + + convertObjectToFormData: object => Object.keys(object).reduce((formData, key) => { formData.append(key, object[key]); return formData; // https://stackoverflow.com/a/62936649/4257237 From 10cc56c71f79ed19d33e1e8684999f8def69797c Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 14:41:21 -0700 Subject: [PATCH 07/13] move DxForm items back to original constructor to avoid confusion --- .../persons/datagrid_attendee_update_view.js | 285 +++++++++--------- 1 file changed, 141 insertions(+), 144 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 1af1fd23..4c265256 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -117,7 +117,6 @@ Attendees.datagridUpdate = { $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); - Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", Attendees.datagridUpdate.attendeeFormBasicInfoItems); Attendees.datagridUpdate.initListeners(); }, error : (response) => { @@ -224,7 +223,147 @@ Attendees.datagridUpdate = { itemType: "group", name: "basic-info-container", caption: "Basic info. Fields after nick name can be removed by clearing & save.", // adding element in caption by $("", {text:"hi 5"}).appendTo($("span.dx-form-group-caption")[1]) - items: [], // will trigger later for dynamic items + items: [ + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', + }, + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), + }, + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", + }, + validationRules: [ + { + type: "required", + message: "gender is required" + }, + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', + }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', + }, + }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', + }, + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', + }, + }, + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, + ], }, { colSpan: 24, @@ -438,148 +577,6 @@ Attendees.datagridUpdate = { ] }, - attendeeFormBasicInfoItems: [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, - }, - { - colSpan: 7, - dataField: "last_name2", - }, - { - colSpan: 7, - dataField: "first_name2", - }, - { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", - }, - validationRules: [ - { - type: "required", - message: "gender is required" - }, - ], - }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - label: { - text: 'Real birthday', - }, - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, - }, - }, - { - colSpan: 7, - dataField: "estimated_birthday", - label: { - text: 'Guess birthday', - }, - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, - }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, - }, - ], - contactPopupDxFormConfig: () => { return { visible: true, From 857d2f69c4f39ed31f07785feca058bb7bef7dc0 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 16:28:13 -0700 Subject: [PATCH 08/13] refactor function to object before implementing server changes --- .../persons/datagrid_attendee_update_view.js | 250 +++++++++--------- attendees/static/js/shared/utilities.js | 4 +- 2 files changed, 125 insertions(+), 129 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 4c265256..fe67ed17 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -127,26 +127,24 @@ Attendees.datagridUpdate = { }, attachContactAddButton: () => { - $("", {class: "extra-contacts"}) + $('', {class: 'extra-contacts', css: {'margin-left': '1rem'}}) .dxButton({ disabled: !Attendees.utilities.editingEnabled, elementAttr: { class: 'attendee-form-submits', // for toggling editing mode }, text:'Add more contact', - icon:"email", - type:"normal", + icon:'email', // or 'fas fa-comment-dots' + stylingMode: 'outlined', + type: "success", height: '1.4rem', hint: 'add more different contacts such as more phones/emails', - onClick: ()=> { - Attendees.datagridUpdate.contactPopup = $("div.popup-more-contacts").dxPopup(Attendees.datagridUpdate.contactPopupDxFormConfig()).dxPopup("instance"); + onClick: () => { + Attendees.datagridUpdate.contactPopup = $('div.popup-more-contacts').dxPopup(Attendees.datagridUpdate.contactPopupDxFormConfig).dxPopup('instance'); }, - }) - .appendTo($("span.dx-form-group-caption")[1]); + }).appendTo($('span.dx-form-group-caption')[1]); // basic info block is at index 1 }, - - attendeeFormConfigs: { readOnly: !Attendees.utilities.editingEnabled, onContentReady: () => { @@ -577,133 +575,131 @@ Attendees.datagridUpdate = { ] }, - contactPopupDxFormConfig: () => { - return { - visible: true, - title: 'Add Contact', - minwidth: "20%", - minheight: "30%", - position: { - my: 'center', - at: 'center', - of: window, - }, - dragEnabled: true, - contentTemplate: (e) => { - const formContainer = $('
'); - Attendees.datagridUpdate.contactPopupDxForm = formContainer.dxForm({ - // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), - // formData: Attendees.datagridUpdate.placeDefaults, - // onFieldDataChanged: (e) => {e.component.validate()}, - scrollingEnabled: true, - showColonAfterLabel: false, - requiredMark: "*", - // labelLocation: "top", - // minColWidth: "20%", - showValidationSummary: true, - items: [ - { - dataField: "contactKey", - editorOptions: { - placeholder: 'for example: WeChat1', + contactPopupDxFormConfig: { + maxWidth: "50%", + maxHeight: "50%", + visible: true, + title: 'Add Contact', + position: { + my: 'center', + at: 'center', + of: window, + }, + dragEnabled: true, + contentTemplate: (e) => { + const formContainer = $('
'); + Attendees.datagridUpdate.contactPopupDxForm = formContainer.dxForm({ + // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), + // formData: Attendees.datagridUpdate.placeDefaults, + // onFieldDataChanged: (e) => {e.component.validate()}, + scrollingEnabled: true, + showColonAfterLabel: false, + requiredMark: "*", + // labelLocation: "top", + // maxWidth: "30%", + showValidationSummary: true, + items: [ + { + dataField: "contactKey", + editorOptions: { + placeholder: 'for example: WeChat1', + }, + helpText: 'Any contact such as email3/phone3/fax1, etc', + label: { + text: 'Contact method', + }, + isRequired: true, + validationRules: [ + { + type: "required", + message: "Contact method is required" }, - helpText: 'Any contact such as email3/phone3/fax1, etc', - label: { - text: 'Contact method', + { + type: "stringLength", + min: 2, + message: "Contact method can't be less than 2 characters" }, - isRequired: true, - validationRules: [ - { - type: "required", - message: "Contact method is required" - }, - { - type: "stringLength", - min: 2, - message: "Contact method can't be less than 2 characters" - }, - { - type: "custom", - message: "That contact method exists already", - validationCallback: (e) => { - const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; - return !Object.keys(currentContacts).includes(e.value.trim()); - } + { + type: "custom", + message: "That contact method exists already", + validationCallback: (e) => { + const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + return !Object.keys(currentContacts).includes(e.value.trim()); } - ], + } + ], + }, + { + dataField: "contactValue", + editorOptions: { + placeholder: 'for example: WeiXin', }, - { - dataField: "contactValue", - editorOptions: { - placeholder: 'for example: WeiXin', + helpText: 'Contact such as name@gmail.com/+15101234567 etc', + label: { + text: 'Contact content', + }, + isRequired: true, + validationRules: [ + { + type: "required", + message: "Contact content is required" }, - helpText: 'Contact such as name@gmail.com/+15101234567 etc', - label: { - text: 'Contact content', + { + type: "stringLength", + min: 2, + message: "Contact content can't be less than 2 characters" }, - isRequired: true, - validationRules: [ - { - type: "required", - message: "Contact content is required" - }, - { - type: "stringLength", - min: 2, - message: "Contact content can't be less than 2 characters" - }, - ], - }, - { - itemType: "button", - // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), - buttonOptions: { - elementAttr: { - class: 'attendee-form-submits', // for toggling editing mode - }, - disabled: !Attendees.utilities.editingEnabled, - text: "Save Custom Contact", - icon: "save", - hint: "save Custom Contact in the popup", - type: "default", - useSubmitBehavior: false, - onClick: (clickEvent) => { - if (Attendees.datagridUpdate.contactPopupDxForm.validate().isValid){ - const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; - const newContact = Attendees.datagridUpdate.contactPopupDxForm.option('formData'); - const trimmedContact = Attendees.utilities.trimBothKeyAndValue(newContact); - - currentContacts[trimmedContact.contactKey]=trimmedContact.contactValue; - newBasicInfoItems = Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container").items.concat({ - colSpan: 7, - dataField: "infos.contacts." + trimmedContact.contactKey, - label: { - text: trimmedContact.contactKey, - }, - }); + ], + }, + { + itemType: "button", + horizontalAlignment: "left", + // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), + buttonOptions: { + elementAttr: { + class: 'attendee-form-submits', // for toggling editing mode + }, + disabled: !Attendees.utilities.editingEnabled, + text: "Save Custom Contact", + icon: "save", + hint: "save Custom Contact in the popup", + type: "default", + useSubmitBehavior: false, + onClick: (e) => { + if (Attendees.datagridUpdate.contactPopupDxForm.validate().isValid){ + const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + const newContact = Attendees.datagridUpdate.contactPopupDxForm.option('formData'); + const trimmedContact = Attendees.utilities.trimBothKeyAndValue(newContact); + + currentContacts[trimmedContact.contactKey] = trimmedContact.contactValue; + const newBasicInfoItems = Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container").items.concat({ + colSpan: 7, + dataField: "infos.contacts." + trimmedContact.contactKey, + label: { + text: trimmedContact.contactKey, + }, + }); - Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", newBasicInfoItems); - Attendees.datagridUpdate.contactPopup.hide(); - - // $.ajax({ - // url : ajaxUrl, - // data : JSON.stringify(userData), - // dataType:'json', - // contentType: "application/json; charset=utf-8", - // method : 'PATCH', - // success: (response) => console.log('599 Success to save data for contact Form in Popup, response: ', response), - // error : (response) => console.log('600 Failed to save data for contact Form in Popup, response: ', response), - // }); - } - }, + Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", newBasicInfoItems); + Attendees.datagridUpdate.contactPopup.hide(); + + // $.ajax({ + // url : ajaxUrl, + // data : JSON.stringify(userData), + // dataType:'json', + // contentType: "application/json; charset=utf-8", + // method : 'PATCH', + // success: (response) => console.log('599 Success to save data for contact Form in Popup, response: ', response), + // error : (response) => console.log('600 Failed to save data for contact Form in Popup, response: ', response), + // }); + } }, }, - - ], - }).dxForm("instance"); - e.append(formContainer); - }, - }; + }, + ], + }).dxForm("instance"); + e.append(formContainer); + }, }, diff --git a/attendees/static/js/shared/utilities.js b/attendees/static/js/shared/utilities.js index 5897f91f..b4f9a2a0 100644 --- a/attendees/static/js/shared/utilities.js +++ b/attendees/static/js/shared/utilities.js @@ -47,10 +47,10 @@ Attendees.utilities = { trimBothKeyAndValue: (obj) => { return Object.entries(obj).reduce((acc, curr) => { const [key, value] = curr; - acc[key.trim()] = value.trim(); + acc[key.trim()] = value.trim(); // acc[key.trim()] = typeof obj[key] == 'string'? obj[key].trim() : trimObj(obj[key]); return acc }, {}); - }, + }, // https://stackoverflow.com/a/33511005/4257237 convertObjectToFormData: object => Object.keys(object).reduce((formData, key) => { From fad9cdc53589d8ec86a45ccd1ff4cef20f896f76 Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 17:30:40 -0700 Subject: [PATCH 09/13] now the extra custom contacts from db can show on page --- .../persons/datagrid_attendee_update_view.js | 302 ++++++++++-------- attendees/static/js/shared/utilities.js | 12 +- 2 files changed, 167 insertions(+), 147 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index fe67ed17..0b633d40 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -117,6 +117,7 @@ Attendees.datagridUpdate = { $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); + Attendees.datagridUpdate.populateBasicInfoBlock(); Attendees.datagridUpdate.initListeners(); }, error : (response) => { @@ -221,147 +222,7 @@ Attendees.datagridUpdate = { itemType: "group", name: "basic-info-container", caption: "Basic info. Fields after nick name can be removed by clearing & save.", // adding element in caption by $("", {text:"hi 5"}).appendTo($("span.dx-form-group-caption")[1]) - items: [ - { - colSpan: 7, - dataField: "first_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "last_name", - editorOptions: { - placeholder: "English", - }, - }, - { - colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", - isRequired: true, - label: { - text: 'Major Division', - }, - editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", - dataSource: new DevExpress.data.DataSource({ - store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", - load: () => { - const d = $.Deferred(); - $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { - d.resolve(response.data); - }); - return d.promise(); - } - }) - }), - }, - }, - { - colSpan: 7, - dataField: "last_name2", - }, - { - colSpan: 7, - dataField: "first_name2", - }, - { - colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", - isRequired: true, - editorOptions: { - dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", - }, - validationRules: [ - { - type: "required", - message: "gender is required" - }, - ], - }, - { - colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", - label: { - text: 'Real birthday', - }, - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'month, day and year are all required', - }, - }, - }, - { - colSpan: 7, - dataField: "estimated_birthday", - label: { - text: 'Guess birthday', - }, - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - elementAttr: { - title: 'pick any day of your best guess year for the age estimation', - }, - }, - }, - { - colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", - editorOptions: { - placeholder: "click calendar", - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.phone1", - label: { - text: 'phone1', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.phone2", - label: { - text: 'phone2', - }, - // editorOptions: {mask: "+1 (X00) 000-0000",} - }, - { - colSpan: 7, - dataField: "infos.contacts.nick_name", - label: { - text: 'nick name', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email1", - label: { - text: 'email1', - }, - }, - { - colSpan: 7, - dataField: "infos.contacts.email2", - label: { - text: 'email2', - }, - }, - ], + items: [], // will populate later for dynamic contacts }, { colSpan: 24, @@ -575,6 +436,165 @@ Attendees.datagridUpdate = { ] }, + populateBasicInfoBlock: () => { + basicInfoItems = [ + { + colSpan: 7, + dataField: "first_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "last_name", + editorOptions: { + placeholder: "English", + }, + }, + { + colSpan: 7, + dataField: "division", + editorType: "dxSelectBox", + isRequired: true, + label: { + text: 'Major Division', + }, + editorOptions: { + valueExpr: "id", + displayExpr: "display_name", + placeholder: "Select a value...", + dataSource: new DevExpress.data.DataSource({ + store: new DevExpress.data.CustomStore({ + key: "id", + loadMode: "raw", + load: () => { + const d = $.Deferred(); + $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { + d.resolve(response.data); + }); + return d.promise(); + } + }) + }), + }, + }, + { + colSpan: 7, + dataField: "last_name2", + }, + { + colSpan: 7, + dataField: "first_name2", + }, + { + colSpan: 7, + dataField: "gender", + editorType: "dxSelectBox", + isRequired: true, + editorOptions: { + dataSource: Attendees.utilities.genderEnums(), + valueExpr: "name", + displayExpr: "name", + }, + validationRules: [ + { + type: "required", + message: "gender is required" + }, + ], + }, + { + colSpan: 7, + dataField: "actual_birthday", + editorType: "dxDateBox", + label: { + text: 'Real birthday', + }, + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'month, day and year are all required', + }, + }, + }, + { + colSpan: 7, + dataField: "estimated_birthday", + label: { + text: 'Guess birthday', + }, + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + elementAttr: { + title: 'pick any day of your best guess year for the age estimation', + }, + }, + }, + { + colSpan: 7, + dataField: "deathday", + editorType: "dxDateBox", + editorOptions: { + placeholder: "click calendar", + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.phone1", + label: { + text: 'phone1', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.phone2", + label: { + text: 'phone2', + }, + // editorOptions: {mask: "+1 (X00) 000-0000",} + }, + { + colSpan: 7, + dataField: "infos.contacts.nick_name", + label: { + text: 'nick name', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email1", + label: { + text: 'email1', + }, + }, + { + colSpan: 7, + dataField: "infos.contacts.email2", + label: { + text: 'email2', + }, + }, + ]; + + const allContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + + for (const contactKey in allContacts) { + if(!(contactKey in Attendees.utilities.basicContacts)){ + basicInfoItems.push({ + colSpan: 7, + dataField: "infos.contacts." + contactKey, + label: { + text: contactKey, + }, + }); + } + } + Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", basicInfoItems); + }, + contactPopupDxFormConfig: { maxWidth: "50%", maxHeight: "50%", diff --git a/attendees/static/js/shared/utilities.js b/attendees/static/js/shared/utilities.js index b4f9a2a0..1a132336 100644 --- a/attendees/static/js/shared/utilities.js +++ b/attendees/static/js/shared/utilities.js @@ -121,12 +121,12 @@ Attendees.utilities = { ]; }, - basicContacts: [ - 'phone1', - 'phone2', - 'email1', - 'email2' - ], + basicContacts: { + phone1: null, + phone2: null, + email1: null, + email2: null, + }, }; $(document).ready(() => { From ed5866cce952146682f8bc7bbf98dedc3e31ec6b Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Fri, 21 May 2021 18:59:09 -0700 Subject: [PATCH 10/13] Instead of POST&GET in HTML FORM, switching api/datagrid_data_attendee API to Restful and update attendee by PUT, so that PATCH can be used for creating custom contacts --- .../attendee_minimal_serializer.py | 23 ++++++++--- attendees/persons/urls.py | 2 +- .../views/api/datagrid_data_attendee.py | 38 +++++++++---------- .../persons/datagrid_attendee_update_view.js | 29 +++++++------- 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/attendees/persons/serializers/attendee_minimal_serializer.py b/attendees/persons/serializers/attendee_minimal_serializer.py index 3e40eff1..4ae6fb25 100644 --- a/attendees/persons/serializers/attendee_minimal_serializer.py +++ b/attendees/persons/serializers/attendee_minimal_serializer.py @@ -51,7 +51,6 @@ def create(self, validated_data): defaults=validated_data, ) - return obj else: return None @@ -61,8 +60,22 @@ def update(self, instance, validated_data): Update and return an existing `AttendingMeet` instance, given the validated data. """ - print("hi AttendeeMinimalSerializer.update() 47 here is validated_data: ") - print(validated_data) - instance.save() - return instance + deleting_photo = self._kwargs['data'].get('photo-clear', None) + + if instance: + old_photo = instance.photo + + if deleting_photo or validated_data.get('photo', None): + if old_photo: + old_file = Path(old_photo.path) + old_file.unlink(missing_ok=True) + if deleting_photo: + validated_data['photo'] = None + + obj, created = Attendee.objects.update_or_create( + id=instance.id, + defaults=validated_data, + ) + + return obj diff --git a/attendees/persons/urls.py b/attendees/persons/urls.py index 37d32ca6..45284735 100644 --- a/attendees/persons/urls.py +++ b/attendees/persons/urls.py @@ -63,7 +63,7 @@ basename='attending', ) router.register( - 'api/datagrid_data_attendee/(?P.+)', + 'api/datagrid_data_attendee', api_datagrid_data_attendee_viewset, basename='attendee', ) diff --git a/attendees/persons/views/api/datagrid_data_attendee.py b/attendees/persons/views/api/datagrid_data_attendee.py index 5c09a1f0..6ec215fa 100644 --- a/attendees/persons/views/api/datagrid_data_attendee.py +++ b/attendees/persons/views/api/datagrid_data_attendee.py @@ -19,33 +19,29 @@ class ApiDatagridDataAttendeeViewSet(LoginRequiredMixin, ModelViewSet): # from serializer_class = AttendeeMinimalSerializer # queryset = Attendee.objects.all() - def retrieve(self, request, *args, **kwargs): - attendee_id = self.request.query_params.get('attendee_id') - print("entering retrieve ... ") - attendee = Attendee.objects.annotate( - joined_meets=JSONBAgg( - Func( - Value('slug'), 'attendings__meets__slug', - Value('display_name'), 'attendings__meets__display_name', - function='jsonb_build_object' - ), - ) - # joined_meets=ArrayAgg('attendings__meets__slug', distinct=True), - ).filter(pk=attendee_id).first() - # attendee = get_object_or_404(queryset) - serializer = AttendeeMinimalSerializer(attendee) - return Response(serializer.data) + # def retrieve(self, request, *args, **kwargs): + # attendee_id = self.kwargs.get('pk') + # attendee = Attendee.objects.annotate( + # joined_meets=JSONBAgg( + # Func( + # Value('attendingmeet_id'), 'attendings__attendingmeet__id', + # Value('attending_finish'), 'attendings__attendingmeet__finish', + # Value('attending_start'), 'attendings__attendingmeet__start', + # Value('meet_name'), 'attendings__meets__display_name', + # function='jsonb_build_object' + # ), + # ), + # # contacts=ArrayAgg('attendings__meets__slug', distinct=True), + # ).filter(pk=attendee_id) + # serializer = AttendeeMinimalSerializer(attendee) + # return Response(serializer.data) def get_queryset(self): """ """ current_user = self.request.user # Todo: guard this API so only admin or scheduler can call it. - querying_attendee_id = self.kwargs.get('attendee_id') - # return AttendeeService.single_record( - # current_user=current_user, - # attendee_id=querying_attendee_id, - # ) + querying_attendee_id = self.kwargs.get('pk') return Attendee.objects.annotate( joined_meets=JSONBAgg( diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 0b633d40..329ff368 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -45,7 +45,8 @@ Attendees.datagridUpdate = { }, toggleEditing: (enabled) => { - $('div.attendee-form-submits, span.attendee-form-submits').dxButton('instance').option('disabled', !enabled); + $('div.attendee-form-submits').dxButton('instance').option('disabled', !enabled); + $('span.attendee-form-submits').dxButton('instance').option('disabled', !enabled); $('button.attendingmeet-button-new, button.family-button-new, button.place-button-new, input.form-check-input').prop('disabled', !enabled); Attendees.datagridUpdate.attendeeMainDxForm.option("readOnly", !enabled); Attendees.datagridUpdate.attendeePhotoFileUploader.option("disabled", !enabled); @@ -113,7 +114,7 @@ Attendees.datagridUpdate = { $.ajax({ url : Attendees.datagridUpdate.attendeeAjaxUrl, success: (response) => { - Attendees.datagridUpdate.attendeeFormConfigs.formData = response.data[0]; + Attendees.datagridUpdate.attendeeFormConfigs.formData = response ? response : {infos:{}}; $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); @@ -397,9 +398,9 @@ Attendees.datagridUpdate = { if (confirm("Are you sure?")){ const userData = new FormData($('form#attendee-update-form')[0]); - if(!$('input[name="photo"]')[0].value){userData.delete("photo")}; + if(!$('input[name="photo"]')[0].value){userData.delete('photo')} userData.set('infos', JSON.stringify(Attendees.datagridUpdate.attendeeFormConfigs.formData.infos)); - userData._method = userData.id ? 'PUT' : 'POST'; + // userData._method = userData.id ? 'PUT' : 'POST'; $.ajax({ url : Attendees.datagridUpdate.attendeeAjaxUrl, @@ -407,7 +408,7 @@ Attendees.datagridUpdate = { processData: false, dataType: 'json', data : userData, - method : 'POST', + method : Attendees.datagridUpdate.attendeeId ? 'PUT' : 'POST', success: (response) => { // Todo: update photo link, temporarily reload to bypass the requirement console.log("success here is response: ", response); const parser = new URL(window.location); @@ -703,15 +704,15 @@ Attendees.datagridUpdate = { Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", newBasicInfoItems); Attendees.datagridUpdate.contactPopup.hide(); - // $.ajax({ - // url : ajaxUrl, - // data : JSON.stringify(userData), - // dataType:'json', - // contentType: "application/json; charset=utf-8", - // method : 'PATCH', - // success: (response) => console.log('599 Success to save data for contact Form in Popup, response: ', response), - // error : (response) => console.log('600 Failed to save data for contact Form in Popup, response: ', response), - // }); + $.ajax({ + url : Attendees.datagridUpdate.attendeeAjaxUrl, + data : JSON.stringify({infos: Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos}), + dataType:'json', + contentType: "application/json; charset=utf-8", + method : 'PATCH', + success: (response) => console.log('712 Success to save data for contact Form in Popup, response: ', response), + error : (response) => console.log('713 Failed to save data for contact Form in Popup, response: ', response), + }); } }, }, From fe48e0b26df34cf8865f809c7a2ba982351b4d8b Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Sat, 22 May 2021 09:01:12 -0700 Subject: [PATCH 11/13] server save contacts works and successfully update UI with deleting empties. The next is to clear last values in new contact popup dxform --- .../attendee_minimal_serializer.py | 6 +- .../persons/datagrid_attendee_update_view.js | 135 ++++++++++-------- attendees/static/js/shared/utilities.js | 11 +- 3 files changed, 84 insertions(+), 68 deletions(-) diff --git a/attendees/persons/serializers/attendee_minimal_serializer.py b/attendees/persons/serializers/attendee_minimal_serializer.py index 4ae6fb25..2aab9268 100644 --- a/attendees/persons/serializers/attendee_minimal_serializer.py +++ b/attendees/persons/serializers/attendee_minimal_serializer.py @@ -37,9 +37,8 @@ def create(self, validated_data): instance = Attendee.objects.get(pk=attendee_id) if instance: - old_photo = instance.photo - if deleting_photo or validated_data.get('photo', None): + old_photo = instance.photo if old_photo: old_file = Path(old_photo.path) old_file.unlink(missing_ok=True) @@ -63,9 +62,8 @@ def update(self, instance, validated_data): deleting_photo = self._kwargs['data'].get('photo-clear', None) if instance: - old_photo = instance.photo - if deleting_photo or validated_data.get('photo', None): + old_photo = instance.photo if old_photo: old_file = Path(old_photo.path) old_file.unlink(missing_ok=True) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 329ff368..779f48de 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -1,7 +1,7 @@ Attendees.datagridUpdate = { attendeeMainDxForm: null, // will be assigned later, may not needed if use native form.submit()? attendeeAttrs: null, // will be assigned later - attendeeId: null, // the attendee is being edited, since it maybe admin/parent editing another attendee + attendeeId: '', // the attendee is being edited, since it maybe admin/parent editing another attendee attendeeAjaxUrl: null, attendeePhotoFileUploader: null, attendingmeetPopupDxForm: null, // for getting formData @@ -13,7 +13,7 @@ Attendees.datagridUpdate = { start: new Date(), finish: new Date().setFullYear(new Date().getFullYear() + 1), // 1 years from now }, - addressId: null, // for sending address data by AJAX + addressId: '', // for sending address data by AJAX placePopup: null, // for show/hide popup placePopupDxForm: null, // for getting formData placePopupDxFormData: {}, // for storing formData @@ -114,7 +114,7 @@ Attendees.datagridUpdate = { $.ajax({ url : Attendees.datagridUpdate.attendeeAjaxUrl, success: (response) => { - Attendees.datagridUpdate.attendeeFormConfigs.formData = response ? response : {infos:{}}; + Attendees.datagridUpdate.attendeeFormConfigs.formData = response ? response : {infos:{contacts:{}}}; $('h3.page-title').text('Details of ' + Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name); window.top.document.title = Attendees.datagridUpdate.attendeeFormConfigs.formData.full_name; Attendees.datagridUpdate.attendeeMainDxForm = $("div.datagrid-attendee-update").dxForm(Attendees.datagridUpdate.attendeeFormConfigs).dxForm("instance"); @@ -437,38 +437,38 @@ Attendees.datagridUpdate = { ] }, - populateBasicInfoBlock: () => { - basicInfoItems = [ + populateBasicInfoBlock: (allContacts=Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos.contacts) => { + const basicInfoItems = [ { colSpan: 7, - dataField: "first_name", + dataField: 'first_name', editorOptions: { - placeholder: "English", + placeholder: 'English', }, }, { colSpan: 7, - dataField: "last_name", + dataField: 'last_name', editorOptions: { - placeholder: "English", + placeholder: 'English', }, }, { colSpan: 7, - dataField: "division", - editorType: "dxSelectBox", + dataField: 'division', + editorType: 'dxSelectBox', isRequired: true, label: { text: 'Major Division', }, editorOptions: { - valueExpr: "id", - displayExpr: "display_name", - placeholder: "Select a value...", + valueExpr: 'id', + displayExpr: 'display_name', + placeholder: 'Select a value...', dataSource: new DevExpress.data.DataSource({ store: new DevExpress.data.CustomStore({ - key: "id", - loadMode: "raw", + key: 'id', + loadMode: 'raw', load: () => { const d = $.Deferred(); $.get(Attendees.datagridUpdate.attendeeAttrs.dataset.divisionsEndpoint).done((response) => { @@ -482,38 +482,38 @@ Attendees.datagridUpdate = { }, { colSpan: 7, - dataField: "last_name2", + dataField: 'last_name2', }, { colSpan: 7, - dataField: "first_name2", + dataField: 'first_name2', }, { colSpan: 7, - dataField: "gender", - editorType: "dxSelectBox", + dataField: 'gender', + editorType: 'dxSelectBox', isRequired: true, editorOptions: { dataSource: Attendees.utilities.genderEnums(), - valueExpr: "name", - displayExpr: "name", + valueExpr: 'name', + displayExpr: 'name', }, validationRules: [ { - type: "required", - message: "gender is required" + type: 'required', + message: 'gender is required' }, ], }, { colSpan: 7, - dataField: "actual_birthday", - editorType: "dxDateBox", + dataField: 'actual_birthday', + editorType: 'dxDateBox', label: { text: 'Real birthday', }, editorOptions: { - placeholder: "click calendar", + placeholder: 'click calendar', elementAttr: { title: 'month, day and year are all required', }, @@ -521,13 +521,13 @@ Attendees.datagridUpdate = { }, { colSpan: 7, - dataField: "estimated_birthday", + dataField: 'estimated_birthday', label: { text: 'Guess birthday', }, - editorType: "dxDateBox", + editorType: 'dxDateBox', editorOptions: { - placeholder: "click calendar", + placeholder: 'click calendar', elementAttr: { title: 'pick any day of your best guess year for the age estimation', }, @@ -535,15 +535,15 @@ Attendees.datagridUpdate = { }, { colSpan: 7, - dataField: "deathday", - editorType: "dxDateBox", + dataField: 'deathday', + editorType: 'dxDateBox', editorOptions: { - placeholder: "click calendar", + placeholder: 'click calendar', }, }, { colSpan: 7, - dataField: "infos.contacts.phone1", + dataField: 'infos.contacts.phone1', label: { text: 'phone1', }, @@ -551,7 +551,7 @@ Attendees.datagridUpdate = { }, { colSpan: 7, - dataField: "infos.contacts.phone2", + dataField: 'infos.contacts.phone2', label: { text: 'phone2', }, @@ -559,41 +559,39 @@ Attendees.datagridUpdate = { }, { colSpan: 7, - dataField: "infos.contacts.nick_name", + dataField: 'infos.contacts.nick_name', label: { text: 'nick name', }, }, { colSpan: 7, - dataField: "infos.contacts.email1", + dataField: 'infos.contacts.email1', label: { text: 'email1', }, }, { colSpan: 7, - dataField: "infos.contacts.email2", + dataField: 'infos.contacts.email2', label: { text: 'email2', }, }, ]; - const allContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; - for (const contactKey in allContacts) { - if(!(contactKey in Attendees.utilities.basicContacts)){ + if(allContacts.hasOwnProperty(contactKey) && !(contactKey in Attendees.utilities.basicContacts)){ basicInfoItems.push({ colSpan: 7, - dataField: "infos.contacts." + contactKey, + dataField: 'infos.contacts.' + contactKey, label: { text: contactKey, }, }); } } - Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", basicInfoItems); + Attendees.datagridUpdate.attendeeMainDxForm.itemOption('basic-info-container', 'items', basicInfoItems); }, contactPopupDxFormConfig: { @@ -688,30 +686,47 @@ Attendees.datagridUpdate = { useSubmitBehavior: false, onClick: (e) => { if (Attendees.datagridUpdate.contactPopupDxForm.validate().isValid){ - const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + const currentInfos = Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos; const newContact = Attendees.datagridUpdate.contactPopupDxForm.option('formData'); const trimmedContact = Attendees.utilities.trimBothKeyAndValue(newContact); - - currentContacts[trimmedContact.contactKey] = trimmedContact.contactValue; - const newBasicInfoItems = Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container").items.concat({ - colSpan: 7, - dataField: "infos.contacts." + trimmedContact.contactKey, - label: { - text: trimmedContact.contactKey, - }, - }); - - Attendees.datagridUpdate.attendeeMainDxForm.itemOption("basic-info-container", "items", newBasicInfoItems); - Attendees.datagridUpdate.contactPopup.hide(); + currentInfos.contacts = Attendees.utilities.trimBothKeyAndValue(currentInfos.contacts); // emove emptied values + currentInfos.contacts[trimmedContact.contactKey] = trimmedContact.contactValue; $.ajax({ url : Attendees.datagridUpdate.attendeeAjaxUrl, - data : JSON.stringify({infos: Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos}), + data : JSON.stringify({infos: currentInfos}), dataType:'json', - contentType: "application/json; charset=utf-8", + contentType: 'application/json; charset=utf-8', method : 'PATCH', - success: (response) => console.log('712 Success to save data for contact Form in Popup, response: ', response), - error : (response) => console.log('713 Failed to save data for contact Form in Popup, response: ', response), + success: (response) => { + console.log('Success to save data for custom contact in Popup, response: ', response); + Attendees.datagridUpdate.populateBasicInfoBlock(response.infos.contacts); + Attendees.datagridUpdate.contactPopup.hide(); + DevExpress.ui.notify( + { + message: "saving custom contact success", + width: 500, + position: { + my: 'center', + at: 'center', + of: window, + } + }, "success", 2500); + }, + error : (response) => { + console.log('Failed to save data for custom contact in Popup, response and infos data: ', response, Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos); + Attendees.datagridUpdate.contactPopup.hide(); + DevExpress.ui.notify( + { + message: "saving custom contact error", + width: 500, + position: { + my: 'center', + at: 'center', + of: window, + } + }, "error", 5000); + }, }); } }, diff --git a/attendees/static/js/shared/utilities.js b/attendees/static/js/shared/utilities.js index 1a132336..0449650c 100644 --- a/attendees/static/js/shared/utilities.js +++ b/attendees/static/js/shared/utilities.js @@ -44,15 +44,18 @@ Attendees.utilities = { }); }, // jQuery toggle() from https://supportcenter.devexpress.com/ticket/details/t525231 - trimBothKeyAndValue: (obj) => { + trimBothKeyAndValue: (obj, keepEmpties=false) => { return Object.entries(obj).reduce((acc, curr) => { const [key, value] = curr; - acc[key.trim()] = value.trim(); // acc[key.trim()] = typeof obj[key] == 'string'? obj[key].trim() : trimObj(obj[key]); - return acc + const trimmedValue = value.trim(); + + if(keepEmpties || trimmedValue) { // Will retain a single empty string as the only one empty key + acc[key.trim()] = trimmedValue; // acc[key.trim()] = typeof obj[key] == 'string'? obj[key].trim() : trimObj(obj[key]); + } + return acc; }, {}); }, // https://stackoverflow.com/a/33511005/4257237 - convertObjectToFormData: object => Object.keys(object).reduce((formData, key) => { formData.append(key, object[key]); return formData; // https://stackoverflow.com/a/62936649/4257237 From 7b79e5667adfda88c75f3be372eef19c3e3451fc Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Sat, 22 May 2021 09:20:29 -0700 Subject: [PATCH 12/13] now the previous coontact values will be removed upon save success --- .../static/js/persons/datagrid_attendee_update_view.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index 779f48de..c52a766a 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -699,7 +699,7 @@ Attendees.datagridUpdate = { contentType: 'application/json; charset=utf-8', method : 'PATCH', success: (response) => { - console.log('Success to save data for custom contact in Popup, response: ', response); + Attendees.datagridUpdate.contactPopupDxForm.resetValues(); Attendees.datagridUpdate.populateBasicInfoBlock(response.infos.contacts); Attendees.datagridUpdate.contactPopup.hide(); DevExpress.ui.notify( @@ -711,7 +711,7 @@ Attendees.datagridUpdate = { at: 'center', of: window, } - }, "success", 2500); + }, 'success', 2500); }, error : (response) => { console.log('Failed to save data for custom contact in Popup, response and infos data: ', response, Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos); @@ -725,7 +725,7 @@ Attendees.datagridUpdate = { at: 'center', of: window, } - }, "error", 5000); + }, 'error', 5000); }, }); } From 6118286011b5482f75c2a7c5a9da7c16f21ff08b Mon Sep 17 00:00:00 2001 From: Jack Lin Date: Sat, 22 May 2021 09:39:17 -0700 Subject: [PATCH 13/13] now main attendee form save also remove emptied custom contacts --- .../persons/datagrid_attendee_update_view.js | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/attendees/static/js/persons/datagrid_attendee_update_view.js b/attendees/static/js/persons/datagrid_attendee_update_view.js index c52a766a..3470c121 100644 --- a/attendees/static/js/persons/datagrid_attendee_update_view.js +++ b/attendees/static/js/persons/datagrid_attendee_update_view.js @@ -399,7 +399,9 @@ Attendees.datagridUpdate = { const userData = new FormData($('form#attendee-update-form')[0]); if(!$('input[name="photo"]')[0].value){userData.delete('photo')} - userData.set('infos', JSON.stringify(Attendees.datagridUpdate.attendeeFormConfigs.formData.infos)); + const userInfos = Attendees.datagridUpdate.attendeeFormConfigs.formData.infos; + userInfos['contacts'] = Attendees.utilities.trimBothKeyAndValue(userInfos.contacts); // remove emptied contacts + userData.set('infos', JSON.stringify(userInfos)); // userData._method = userData.id ? 'PUT' : 'POST'; $.ajax({ @@ -595,8 +597,8 @@ Attendees.datagridUpdate = { }, contactPopupDxFormConfig: { - maxWidth: "50%", - maxHeight: "50%", + maxWidth: '50%', + maxHeight: '50%', visible: true, title: 'Add Contact', position: { @@ -608,18 +610,13 @@ Attendees.datagridUpdate = { contentTemplate: (e) => { const formContainer = $('
'); Attendees.datagridUpdate.contactPopupDxForm = formContainer.dxForm({ - // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), - // formData: Attendees.datagridUpdate.placeDefaults, - // onFieldDataChanged: (e) => {e.component.validate()}, scrollingEnabled: true, showColonAfterLabel: false, - requiredMark: "*", - // labelLocation: "top", - // maxWidth: "30%", + requiredMark: '*', showValidationSummary: true, items: [ { - dataField: "contactKey", + dataField: 'contactKey', editorOptions: { placeholder: 'for example: WeChat1', }, @@ -630,66 +627,65 @@ Attendees.datagridUpdate = { isRequired: true, validationRules: [ { - type: "required", - message: "Contact method is required" + type: 'required', + message: 'Contact method is required' }, { - type: "stringLength", + type: 'stringLength', min: 2, message: "Contact method can't be less than 2 characters" }, { - type: "custom", - message: "That contact method exists already", + type: 'custom', + message: 'That contact method exists already', validationCallback: (e) => { - const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option("formData").infos.contacts; + const currentContacts = Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos.contacts; return !Object.keys(currentContacts).includes(e.value.trim()); } } ], }, { - dataField: "contactValue", + dataField: 'contactValue', editorOptions: { placeholder: 'for example: WeiXin', }, - helpText: 'Contact such as name@gmail.com/+15101234567 etc', + helpText: 'Contact such as name@email.com/+15101234567 etc', label: { text: 'Contact content', }, isRequired: true, validationRules: [ { - type: "required", - message: "Contact content is required" + type: 'required', + message: 'Contact content is required' }, { - type: "stringLength", + type: 'stringLength', min: 2, message: "Contact content can't be less than 2 characters" }, ], }, { - itemType: "button", - horizontalAlignment: "left", - // disable: !(Attendees.datagridUpdate.contactPopupDxForm && Attendees.datagridUpdate.contactPopupDxForm.validate().isValid), + itemType: 'button', + horizontalAlignment: 'left', buttonOptions: { elementAttr: { class: 'attendee-form-submits', // for toggling editing mode }, disabled: !Attendees.utilities.editingEnabled, - text: "Save Custom Contact", - icon: "save", + text: 'Save Custom Contact', + icon: 'save', hint: "save Custom Contact in the popup", - type: "default", + type: 'default', useSubmitBehavior: false, onClick: (e) => { if (Attendees.datagridUpdate.contactPopupDxForm.validate().isValid){ const currentInfos = Attendees.datagridUpdate.attendeeMainDxForm.option('formData').infos; const newContact = Attendees.datagridUpdate.contactPopupDxForm.option('formData'); const trimmedContact = Attendees.utilities.trimBothKeyAndValue(newContact); - currentInfos.contacts = Attendees.utilities.trimBothKeyAndValue(currentInfos.contacts); // emove emptied values + currentInfos.contacts = Attendees.utilities.trimBothKeyAndValue(currentInfos.contacts); // remove emptied contacts currentInfos.contacts[trimmedContact.contactKey] = trimmedContact.contactValue; $.ajax({ @@ -1004,7 +1000,6 @@ Attendees.datagridUpdate = { initPlacePopupDxForm: (event) => { const placeButton = event.target; - console.log("hi 787 here is placeButton: ", placeButton); Attendees.datagridUpdate.placePopup = $('div.popup-place-update').dxPopup(Attendees.datagridUpdate.locatePopupDxFormConfig(placeButton)).dxPopup('instance'); Attendees.datagridUpdate.fetchLocateFormData(placeButton); },