From 7af62ca005955f581d9e6bb8119c4cbb530d1463 Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Mon, 23 Sep 2019 19:50:29 +0530 Subject: [PATCH 01/54] GCW-2457 added validation feild on district field --- app/styles/_request_purpose.scss | 95 ++++++++++++++----- app/templates/order/request_purpose.hbs | 117 ++++++++++++------------ 2 files changed, 130 insertions(+), 82 deletions(-) diff --git a/app/styles/_request_purpose.scss b/app/styles/_request_purpose.scss index 3a11d16f4..637e3527e 100644 --- a/app/styles/_request_purpose.scss +++ b/app/styles/_request_purpose.scss @@ -1,13 +1,16 @@ -.request-purpose, .client-information { - .button{ +.request-purpose, +.client-information { + .button { margin-top: 1rem; } + .input-error { background: #ED4C4E !important; color: white; padding: 0.2rem; display: none; padding-bottom: 0.25rem !important; + @media #{$small-only} { font-size: 0.65rem; width: 100%; @@ -26,14 +29,15 @@ } .form__control.form__control--error .input-error { - .input-error{ + .input-error { display: block !important; } } - .select.form__control--error > #district-select-dropdown{ + .select.form__control--error>#district-select-dropdown { border-color: #f34d4f; background: #fddbdc; + margin-bottom: 0; } .request_purpose_title { @@ -48,9 +52,10 @@ margin-right: 0; .custom_radio_buttons { - @media #{$small-only}{ + @media #{$small-only} { width: 100% !important; } + display: inline-block; position: relative; border: 1px solid; @@ -73,12 +78,14 @@ width: 2em; height: 2em; } + label { font-size: 0.8rem !important; margin-top: 0.4rem !important; margin-left: 3rem; } } + @media #{$small-only} { font-size: 0.65rem; } @@ -96,15 +103,18 @@ padding: .4rem; margin-top: 1rem; height: 2.7rem; + // border: 1px solid white !important; input[type=radio] { margin: 0 0 0 0px; } + label { font-size: 0.8rem !important; margin-top: 0.4rem !important; } } + @media #{$small-only} { font-size: 0.65rem; width: 98%; @@ -117,22 +127,25 @@ } } - .time-slots{ + .time-slots { .custom_radio_buttons { border: 1px solid; padding: .4rem; margin-top: 1rem; height: 2.7rem; + // border: 1px solid white !important; input[type=radio] { margin: 0 0 0 0px; } + label { - @media #{$large-only}{ + @media #{$large-only} { font-size: 0.8rem !important; margin-top: 0.4rem !important; } - @media #{$small-only}{ + + @media #{$small-only} { margin-left: 0 !important; margin-right: 0 !important; font-size: 0.6rem !important; @@ -142,12 +155,12 @@ } } - .people-count{ + .people-count { padding-left: 0rem !important; margin-left: 0; margin-right: 0; - .people-count-label{ + .people-count-label { margin-top: 1rem !important; color: white; font-size: 0.9rem; @@ -155,6 +168,7 @@ margin-top: 1rem; margin-bottom: 0.5rem; } + input { height: 2.7rem; padding: 0 0.5rem; @@ -176,14 +190,17 @@ margin-top: 1rem; margin-bottom: 0.2rem; } - .reason-of-need{ + + .reason-of-need { font-size: 0.8rem; margin-bottom: 0.5rem; } + textarea { - @media #{$small-only}{ + @media #{$small-only} { max-width: 100%; } + padding: 0.3rem 0.5rem; background: #002352; border: 1px solid $tabbar-menu-icon-color; @@ -192,13 +209,16 @@ margin-bottom: 0; height: 5rem !important; } - .remove-text{ + + .remove-text { position: absolute; right: 1%; - @media #{$small-only}{ + + @media #{$small-only} { right: 3%; top: 0%; } + font-size: 1rem; cursor: pointer; } @@ -210,26 +230,28 @@ } } -.success-submit{ +.success-submit { background: cornflowerblue !important; color: white !important; text-transform: capitalize !important; } -.no-gc-req-error, .no-time-slot-selected-error { +.no-gc-req-error, +.no-time-slot-selected-error { color: #ee4f4b; } -.book_appointment_headers{ - @media #{$small-only}{ +.book_appointment_headers { + @media #{$small-only} { width: 96% } } -.labor_confirmation{ - @media #{$small-only}{ +.labor_confirmation { + @media #{$small-only} { font-size: 1em; } + width: 110%; } @@ -250,26 +272,29 @@ content: none !important; } - #district-select-dropdown{ + #district-select-dropdown { label.select { -webkit-appearance: menulist-button; } - @media #{$small-only}{ + @media #{$small-only} { margin-left: -5px; } + border: 1px solid #8091a9; background: bottom; color: #a3aab6; margin-left: 0% !important; } + .district-label { color: white; font-size: 0.9rem; margin-top: 1rem; margin-bottom: 0.2rem; } - .district-label-info{ + + .district-label-info { font-size: 0.8rem; margin-bottom: 0.5rem; } @@ -308,7 +333,7 @@ font-size: 0.9rem; font-weight: 700; - @media #{$small-only}{ + @media #{$small-only} { font-size: 0.7rem; } } @@ -322,3 +347,25 @@ margin-top: -2%; } +.select-error-message { + display: none; + background: #ED4C4E; + color: white; + padding: 0.2rem; + display: none; + padding-bottom: 0.25rem; + width: 100%; + + span { + margin-left: 0.7rem; + } + + @media #{$small-only} { + font-size: 0.65rem; + } +} + +.select.form__control--error>#district-select-dropdown+.select-error-message { + display: block; + float: left; +} diff --git a/app/templates/order/request_purpose.hbs b/app/templates/order/request_purpose.hbs index 12a11262f..7a3300e50 100644 --- a/app/templates/order/request_purpose.hbs +++ b/app/templates/order/request_purpose.hbs @@ -22,70 +22,71 @@ buttonId='#request-submit' }} {{#validatable-form onSubmit=(action 'createOrderWithRequestPuropose') as |form|}} -
-
-
{{t 'order.request_purpose.purpose'}}
-
- {{t 'order.request_purpose.people_count_info'}} - {{#form-control}} - {{input type='number' min="1" value=peopleHelped required='true' id="people-count"}} -
- {{fa-icon "exclamation-triangle"}} - - {{t 'order.request_purpose.people_count_warning'}} - -
- {{/form-control}} +
+
+
{{t 'order.request_purpose.purpose'}}
+
+ {{t 'order.request_purpose.people_count_info'}} + {{#form-control}} + {{input type='number' min="1" value=peopleHelped required='true' id="people-count"}} +
+ {{fa-icon "exclamation-triangle"}} + + {{t 'order.request_purpose.people_count_warning'}} +
+ {{/form-control}}
-
-
- {{t 'order.request_purpose.district_label'}} -
-
- {{t 'order.request_purpose.district_label_info'}} -
- {{#form-control}} - {{select-list - content=districts - on-change=(action (mut selectedDistrict)) - selectedValue=selectedDistrict - prompt=(t 'order.request_purpose.select_placeholder') - value= selectedDistrict - selectArrow=true - required=true - classNames='district-selector' - idNames='district-select-dropdown'}} - {{/form-control}} -
+
+
+
+ {{t 'order.request_purpose.district_label'}}
-
-
- {{t 'order.request_purpose.need_description'}} -
-
- {{t 'order.request_purpose.need_description_info'}} -
-
- {{#form-control}} - - {{fa-icon 'times-circle'}} - - {{auto-resize-textarea data-autoresize=true name="description" value=order.purposeDescription required="required" pattern=".*\S.*" id="description"}} -
- {{fa-icon "exclamation-triangle"}} - - {{t 'order.request_purpose.description_warning'}} - -
- {{/form-control}} -
+
+ {{t 'order.request_purpose.district_label_info'}} +
+ {{#form-control}} + {{select-list + content=districts + on-change=(action (mut selectedDistrict)) + selectedValue=selectedDistrict + prompt=(t 'order.request_purpose.select_placeholder') + value= selectedDistrict + selectArrow=true + required=true + errorMessage = "You must select the district" + classNames='district-selector' + idNames='district-select-dropdown'}} + {{/form-control}} +
+
+
+
+ {{t 'order.request_purpose.need_description'}}
-
-
- {{t 'continue'}} +
+ {{t 'order.request_purpose.need_description_info'}} +
+
+ {{#form-control}} + + {{fa-icon 'times-circle'}} + + {{auto-resize-textarea data-autoresize=true name="description" value=order.purposeDescription required="required" pattern=".*\S.*" id="description"}} +
+ {{fa-icon "exclamation-triangle"}} + + {{t 'order.request_purpose.description_warning'}} +
+ {{/form-control}} +
+
+ {{/validatable-form}}
From 0ab32e40540fba1758e5b1204debb9c90be0379d Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Mon, 23 Sep 2019 20:39:54 +0530 Subject: [PATCH 02/54] GCW-2457 added validation to date picket field --- app/styles/_appointment_details.scss | 79 +++++---- app/templates/order/appointment_details.hbs | 187 ++++++++++---------- 2 files changed, 143 insertions(+), 123 deletions(-) diff --git a/app/styles/_appointment_details.scss b/app/styles/_appointment_details.scss index 77c80fcaf..3ae8e8619 100644 --- a/app/styles/_appointment_details.scss +++ b/app/styles/_appointment_details.scss @@ -1,4 +1,4 @@ -.appointment-date{ +.appointment-date { .calender_icon { margin-right: 1.1rem; font-size: 1.1rem; @@ -7,53 +7,68 @@ float: right; margin-top: -3rem; } + i.calendar_icon:before { position: absolute; } - .checkbox.form__control--error{ + + .checkbox.form__control--error { border-color: #f34d4f; background: #fddbdc; } + .time_slots_radio_buttons { - display: inline-block; - position: relative; - border: 1px solid; - padding: .4rem; - margin-top: 1rem; - height: 2.7rem; - width: 32%; - margin-right: 2px; + display: inline-block; + position: relative; + border: 1px solid; + padding: .4rem; + margin-top: 1rem; + height: 2.7rem; + width: 32%; + margin-right: 2px; - input[type=radio] { - margin: 0 0 0 0px; - display: inline-block; - margin-left: 10px; - margin-top: 4% !important; - position: absolute; - height: 1.2rem; - width: 1.2rem; - } + input[type=radio] { + margin: 0 0 0 0px; + display: inline-block; + margin-left: 10px; + margin-top: 4% !important; + position: absolute; + height: 1.2rem; + width: 1.2rem; + } - .radio-label { - margin-top: -5%; - } + .radio-label { + margin-top: -5%; + } - label { - font-size: 0.8rem !important; - margin-top: 0.4rem !important; - margin-left: 3rem; + label { + font-size: 0.8rem !important; + margin-top: 0.4rem !important; + margin-left: 3rem; - @media #{$small-only} { - margin-left: 2rem; - } + @media #{$small-only} { + margin-left: 2rem; } } + } } .time_inputs { - .form__control--error > .pickadate{ - border-color: #f34d4f; - background: #fddbdc; + .form__control--error { + &>.pickadate { + border-color: #f34d4f; + background: #fddbdc; + margin-bottom: 0; + } + + .select-error-message { + display: block; + float: left; + } + + .calender_icon { + margin-top: -2rem; + } } } diff --git a/app/templates/order/appointment_details.hbs b/app/templates/order/appointment_details.hbs index fd1e07916..eb4eaba98 100644 --- a/app/templates/order/appointment_details.hbs +++ b/app/templates/order/appointment_details.hbs @@ -10,7 +10,7 @@ {{fa-icon "angle-left"}} {{#link-to 'order.goods_details' order.id classNames="back_text"}}{{t "back"}}{{/link-to}} -
+

{{t 'order.appointment.title'}}

{{cancel-order order=order}} @@ -18,79 +18,80 @@
{{#validate-appointment}} - {{validate-inputs + {{validate-inputs elementCssIdentifiersArrayJson='["#selectedDate input"]' errorClass='form__control--error' buttonId='#appointment-submit' }} - {{#validatable-form onSubmit=(action 'saveTransportDetails') as |form|}} -
- {{partial "order/user_information"}} -
- {{t 'order.appointment.transport'}} -
-
-
- {{radio-button + {{#validatable-form onSubmit=(action 'saveTransportDetails') as |form|}} +
+ {{partial "order/user_information"}} +
+ {{t 'order.appointment.transport'}} +
+
+
+ {{radio-button name="selectReason" id="self" value="self" selection=selectedId required='true'}} - -
+ +
-
- {{radio-button +
+ {{radio-button name="selectReason" id="ggv" value="ggv" selection=selectedId required='true'}} - -
-
-
+ +
+
+
-
- {{t 'order.appointment.confirm_labor'}} -
+
+ {{t 'order.appointment.confirm_labor'}} +
-
- {{t 'order.appointment.labor_info'}} -
-
-
- {{input type="checkbox" name="labor" checked=false id='4' required=true}} - -
-
-
+
+ {{t 'order.appointment.labor_info'}} +
+
+
+ {{input type="checkbox" name="labor" checked=false id='4' required=true}} + +
+
+
-
-
- {{t 'order.appointment.date'}} -
-
+
+
+ {{t 'order.appointment.date'}} +
+
-
- {{#form-control}} -
- {{select-appointment-date +
+ {{#form-control}} +
+ {{select-appointment-date name='selectedDate' id='selectedDate' value='' @@ -98,50 +99,54 @@ selection=selectedDate placeholder="Date" pattern="[a-zA-Z]{3}\s?[a-zA-Z]{3}\s?[0-9]{1,2}"}} - {{fa-icon "calendar"}} -
- {{/form-control}} + {{fa-icon "calendar"}} +
+ {{fa-icon "exclamation-triangle"}} + You Must select the appointment date
+
+ {{/form-control}} +
-
- {{#if selectedDate}} -
- {{t 'order.appointment.time'}} -
-
-
- {{#each timeSlots as |time|}} - {{#unless time.isClosed}} -
- {{radio-button +
+ {{#if selectedDate}} +
+ {{t 'order.appointment.time'}} +
+
+
+ {{#each timeSlots as |time|}} + {{#unless time.isClosed}} +
+ {{radio-button name="selectTime" id=time.timestamp value=time.timestamp selection=selectedTimeId required='true'}} - -
- {{/unless}} - {{/each}} -
-
- {{#if timeSlotNotSelected}} -

- {{t "order.goods_details.no_time_slot_selected_error"}} -

- {{/if}} - {{/if}} -
- +
-
- {{/validatable-form}} + {{/unless}} + {{/each}} +
+
+ {{#if timeSlotNotSelected}} +

+ {{t "order.goods_details.no_time_slot_selected_error"}} +

+ {{/if}} + {{/if}} + + + {{/validatable-form}} {{/validate-appointment}}
From f4ad16dfd87a4719e981030bb2bece47536cd7f3 Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Mon, 23 Sep 2019 20:56:51 +0530 Subject: [PATCH 03/54] GCW-2457 added error in the translation file --- app/locales/en/translations.js | 6 ++++-- app/locales/zh-tw/translations.js | 6 ++++-- app/templates/order/appointment_details.hbs | 2 +- app/templates/order/request_purpose.hbs | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/locales/en/translations.js b/app/locales/en/translations.js index 0232d9a73..3ad8edbd9 100644 --- a/app/locales/en/translations.js +++ b/app/locales/en/translations.js @@ -356,7 +356,8 @@ export default { people_count_warning: "You must input a number here", need_description: "Description of need", need_description_info: "Please briefly describe why goods are needed.", - description_warning: "Description cannot be blank." + description_warning: "Description cannot be blank.", + error_message: "You must select the district" }, goods_details: { title: "Goods Details", @@ -385,7 +386,8 @@ export default { "If the request is too large for the client to carry alone, please advise them to bring their own labor to load at our end and unload at their end. Crossroads does not provide labor, and hired truck drivers do not provide loading services without charge.", labor_confirmation: "I confirm understanding of labor requirements.", date: "Appointment Date", - time: "Appointment Time" + time: "Appointment Time", + error_message: "You Must select the appointment date" }, booking_success: { success: "Success!", diff --git a/app/locales/zh-tw/translations.js b/app/locales/zh-tw/translations.js index 6754be801..ef7e739f4 100644 --- a/app/locales/zh-tw/translations.js +++ b/app/locales/zh-tw/translations.js @@ -344,7 +344,8 @@ export default { people_count_warning: "您必須填寫一個數字於此欄", need_description: "需要的原因", need_description_info: "請簡單填寫為什麼需要這些物品", - description_warning: "此欄不能留空" + description_warning: "此欄不能留空", + error_message: "You must select the district" }, goods_details: { title: "物品資料", @@ -373,7 +374,8 @@ export default { "如受惠者申請的物品體積龐大,請建議他/她帶備足夠人手以在十字路會提貨及到達目的地時卸貨。十字路會未能提供搬運工人,而聘用車輛司機協助搬運是需要額外收費的。", labor_confirmation: "我確認已清楚明白搬運工人的要求", date: "預約日期", - time: "預約時間" + time: "預約時間", + error_message: "You Must select the appointment date" }, booking_success: { success: "成功!", diff --git a/app/templates/order/appointment_details.hbs b/app/templates/order/appointment_details.hbs index eb4eaba98..fcdc81b5b 100644 --- a/app/templates/order/appointment_details.hbs +++ b/app/templates/order/appointment_details.hbs @@ -102,7 +102,7 @@ {{fa-icon "calendar"}}
{{fa-icon "exclamation-triangle"}} - You Must select the appointment date + {{t 'order.appointment.error_message'}}
{{/form-control}} diff --git a/app/templates/order/request_purpose.hbs b/app/templates/order/request_purpose.hbs index 7a3300e50..f9cc5fe09 100644 --- a/app/templates/order/request_purpose.hbs +++ b/app/templates/order/request_purpose.hbs @@ -54,7 +54,7 @@ value= selectedDistrict selectArrow=true required=true - errorMessage = "You must select the district" + errorMessage = (t 'order.request_purpose.error_message') classNames='district-selector' idNames='district-select-dropdown'}} {{/form-control}} From 27013d3607c4c264bcb343e1634f17a4edfa3b4b Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Mon, 23 Sep 2019 21:12:50 +0530 Subject: [PATCH 04/54] GCW-2457 code formatting --- app/templates/order/appointment_details.hbs | 210 ++++++++++---------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/app/templates/order/appointment_details.hbs b/app/templates/order/appointment_details.hbs index fcdc81b5b..c32200c4f 100644 --- a/app/templates/order/appointment_details.hbs +++ b/app/templates/order/appointment_details.hbs @@ -18,135 +18,135 @@ {{#validate-appointment}} - {{validate-inputs - elementCssIdentifiersArrayJson='["#selectedDate input"]' - errorClass='form__control--error' - buttonId='#appointment-submit' - }} - {{#validatable-form onSubmit=(action 'saveTransportDetails') as |form|}} -
- {{partial "order/user_information"}} -
- {{t 'order.appointment.transport'}} -
-
-
- {{radio-button + {{validate-inputs + elementCssIdentifiersArrayJson='["#selectedDate input"]' + errorClass='form__control--error' + buttonId='#appointment-submit' + }} + {{#validatable-form onSubmit=(action 'saveTransportDetails') as |form|}} +
+ {{partial "order/user_information"}} +
+ {{t 'order.appointment.transport'}} +
+
+
+ {{radio-button name="selectReason" id="self" value="self" selection=selectedId required='true'}} - -
+ +
-
- {{radio-button +
+ {{radio-button name="selectReason" id="ggv" value="ggv" selection=selectedId required='true'}} - + +
-
-
+
-
- {{t 'order.appointment.confirm_labor'}} -
- -
- {{t 'order.appointment.labor_info'}} -
-
-
- {{input type="checkbox" name="labor" checked=false id='4' required=true}} - +
+ {{t 'order.appointment.confirm_labor'}}
-
-
-
-
- {{t 'order.appointment.date'}} +
+ {{t 'order.appointment.labor_info'}}
-
+
+
+ {{input type="checkbox" name="labor" checked=false id='4' required=true}} + +
+
+
-
- {{#form-control}} -
- {{select-appointment-date - name='selectedDate' - id='selectedDate' - value='' - available=available_dates - selection=selectedDate - placeholder="Date" - pattern="[a-zA-Z]{3}\s?[a-zA-Z]{3}\s?[0-9]{1,2}"}} - {{fa-icon "calendar"}} -
- {{fa-icon "exclamation-triangle"}} - {{t 'order.appointment.error_message'}} +
+
+ {{t 'order.appointment.date'}}
- {{/form-control}} -
-
- {{#if selectedDate}} -
- {{t 'order.appointment.time'}} -
-
-
- {{#each timeSlots as |time|}} - {{#unless time.isClosed}} -
- {{radio-button - name="selectTime" - id=time.timestamp - value=time.timestamp - selection=selectedTimeId - required='true'}} - +
+ {{#form-control}} +
+ {{select-appointment-date + name='selectedDate' + id='selectedDate' + value='' + available=available_dates + selection=selectedDate + placeholder="Date" + pattern="[a-zA-Z]{3}\s?[a-zA-Z]{3}\s?[0-9]{1,2}"}} + {{fa-icon "calendar"}} +
+ {{fa-icon "exclamation-triangle"}} + {{t 'order.appointment.error_message'}} +
- {{/unless}} - {{/each}} + {{/form-control}} +
+ +
+ {{#if selectedDate}} +
+ {{t 'order.appointment.time'}}
-
- {{#if timeSlotNotSelected}} -

- {{t "order.goods_details.no_time_slot_selected_error"}} -

- {{/if}} - {{/if}} -
- - {{t 'continue'}} - +
+ {{#each timeSlots as |time|}} + {{#unless time.isClosed}} +
+ {{radio-button + name="selectTime" + id=time.timestamp + value=time.timestamp + selection=selectedTimeId + required='true'}} + +
+ {{/unless}} + {{/each}} +
-
-
- {{/validatable-form}} + {{#if timeSlotNotSelected}} +

+ {{t "order.goods_details.no_time_slot_selected_error"}} +

+ {{/if}} + {{/if}} + + + {{/validatable-form}} {{/validate-appointment}} From 933feaf04e09a0e18a7e1eb8dbdd80c76764376f Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Wed, 25 Sep 2019 13:14:27 +0530 Subject: [PATCH 05/54] GCW-2457 create a new vaibale for color in settings file --- app/styles/_appointment_details.scss | 2 +- app/styles/_request_purpose.scss | 4 ++-- app/styles/_settings.scss | 23 +++++++++++++++-------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/styles/_appointment_details.scss b/app/styles/_appointment_details.scss index 3ae8e8619..cd98c9ee7 100644 --- a/app/styles/_appointment_details.scss +++ b/app/styles/_appointment_details.scss @@ -28,7 +28,7 @@ margin-right: 2px; input[type=radio] { - margin: 0 0 0 0px; + margin: 0; display: inline-block; margin-left: 10px; margin-top: 4% !important; diff --git a/app/styles/_request_purpose.scss b/app/styles/_request_purpose.scss index 637e3527e..20b867508 100644 --- a/app/styles/_request_purpose.scss +++ b/app/styles/_request_purpose.scss @@ -349,8 +349,8 @@ .select-error-message { display: none; - background: #ED4C4E; - color: white; + background: $cherry-red; + color: $white; padding: 0.2rem; display: none; padding-bottom: 0.25rem; diff --git a/app/styles/_settings.scss b/app/styles/_settings.scss index a6405203c..2de38083f 100644 --- a/app/styles/_settings.scss +++ b/app/styles/_settings.scss @@ -124,7 +124,8 @@ $light-gray: #787878; $light-gray-1: #315E81; $light-purple: #c2b8d0; -$secondary-color: rgba(0,0,0,0); +$cherry-red: #ED4C4E; +$secondary-color: rgba(0, 0, 0, 0); $light-grey: #dee4eb; $org-grey: #bab3b3; $light-yellow: #ffdcac; @@ -145,7 +146,13 @@ $body-bg: $dark-blue; // $body-font-color: $jet; $body-font-color: #a3aab6; // $body-font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; -$body-font-family: MuseoSans300, Museo300, "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; +$body-font-family: MuseoSans300, +Museo300, +"Helvetica Neue", +"Helvetica", +Helvetica, +Arial, +sans-serif; // $body-font-weight: $font-weight-normal; // $body-font-style: normal; @@ -732,9 +739,9 @@ $input-border-width: 2px; // We use these to style the prefix and postfix input elements // $input-prefix-bg: scale-color($white, $lightness: -5%); -$input-prefix-bg: rgba(0,0,0,0); +$input-prefix-bg: rgba(0, 0, 0, 0); // $input-prefix-border-color: scale-color($white, $lightness: -20%); -$input-prefix-border-color: rgba(0,0,0,0); +$input-prefix-border-color: rgba(0, 0, 0, 0); // $input-prefix-border-size: 1px; $input-prefix-border-size: 0; // $input-prefix-border-type: solid; @@ -771,12 +778,12 @@ $input-error-message-font-size: rem-calc(18); // We use these to style the icon-bar and items // $include-html-icon-bar-classes: $include-html-classes; // $icon-bar-bg: $oil; -$icon-bar-bg: rgba(0,0,0,0); +$icon-bar-bg: rgba(0, 0, 0, 0); // $icon-bar-font-color: $white; $icon-bar-font-color: #a3aab6; // $icon-bar-font-size: 1rem; // $icon-bar-hover-color: $primary-color; -$icon-bar-hover-color: rgba(0,0,0,0); +$icon-bar-hover-color: rgba(0, 0, 0, 0); // $icon-bar-icon-color: $white; // $icon-bar-icon-size: 1.875rem; $icon-bar-icon-size: 1.5rem; @@ -923,7 +930,7 @@ $off-canvas-bg: #0d1b2f; // Off Canvas Menu List Variables // $off-canvas-label-padding: 0.3rem rem-calc(15); -$off-canvas-label-padding: rem-calc(12, 20); +$off-canvas-label-padding: rem-calc(12, 20); // $off-canvas-label-color: $aluminum; // $off-canvas-label-text-transform: uppercase; $off-canvas-label-text-transform: none; @@ -931,7 +938,7 @@ $off-canvas-label-text-transform: none; // $off-canvas-label-font-weight: $font-weight-bold; $off-canvas-label-font-weight: normal; // $off-canvas-label-bg: $tuatara; -$off-canvas-label-bg: rgba(0,0,0,0); +$off-canvas-label-bg: rgba(0, 0, 0, 0); // $off-canvas-label-border-top: 1px solid scale-color($tuatara, $lightness: 14%); // $off-canvas-label-border-bottom: none; // $off-canvas-label-margin:0; From cf95c060f19e7ae430e332b8a754dae68e40d60c Mon Sep 17 00:00:00 2001 From: Abhinav Garg Date: Wed, 25 Sep 2019 21:01:33 +0530 Subject: [PATCH 06/54] GCW-2457 added chinese translation for error messges --- app/locales/zh-tw/translations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/zh-tw/translations.js b/app/locales/zh-tw/translations.js index ef7e739f4..2bf416709 100644 --- a/app/locales/zh-tw/translations.js +++ b/app/locales/zh-tw/translations.js @@ -345,7 +345,7 @@ export default { need_description: "需要的原因", need_description_info: "請簡單填寫為什麼需要這些物品", description_warning: "此欄不能留空", - error_message: "You must select the district" + error_message: "你需要選擇地區" }, goods_details: { title: "物品資料", @@ -375,7 +375,7 @@ export default { labor_confirmation: "我確認已清楚明白搬運工人的要求", date: "預約日期", time: "預約時間", - error_message: "You Must select the appointment date" + error_message: "你需要選擇約定日期" }, booking_success: { success: "成功!", From c9b6e4b07a43115dd3f8d5559fe93b047a52b55c Mon Sep 17 00:00:00 2001 From: swatijadhav Date: Tue, 1 Oct 2019 11:29:00 +0530 Subject: [PATCH 07/54] refactored model code --- app/instance-initializers/ajax.js | 4 +--- app/models/beneficiary.js | 4 ++-- app/models/cloudinary_url.js | 4 +++- app/models/code.js | 8 +------- app/models/designation.js | 20 +++++------------- app/models/item.js | 13 ++++-------- app/models/orders_package.js | 8 ++------ app/models/package_type.js | 3 ++- app/models/set_item.js | 34 ++++++++++--------------------- app/models/user_profile.js | 6 +----- tests/unit/model/set_item-test.js | 2 +- 11 files changed, 33 insertions(+), 73 deletions(-) diff --git a/app/instance-initializers/ajax.js b/app/instance-initializers/ajax.js index f638c6831..9f932366a 100644 --- a/app/instance-initializers/ajax.js +++ b/app/instance-initializers/ajax.js @@ -6,8 +6,6 @@ export default { const { container = app } = app; const adapter = container.lookup("adapter:application"); - AjaxPromise.setDefaultHeaders(() => { - return adapter.get("headers"); - }); + AjaxPromise.setDefaultHeaders(() => adapter.get("headers")); } }; diff --git a/app/models/beneficiary.js b/app/models/beneficiary.js index cf89c628f..10b4fc4cf 100644 --- a/app/models/beneficiary.js +++ b/app/models/beneficiary.js @@ -13,7 +13,7 @@ export default Model.extend({ identityType: belongsTo('identityType', { async: false }), fullName: Ember.computed('firstName', 'lastName', function(){ - return (this.get("title") + " " + this.get('firstName') + " " + this.get('lastName')); + return `${this.get("title")} ${this.get('firstName')} ${this.get('lastName')}`; }), mobileWithoutCountryCode: Ember.computed('mobile', function(){ @@ -22,6 +22,6 @@ export default Model.extend({ }), fullNameWithoutTitle: Ember.computed('firstName', 'lastName', function(){ - return this.get('firstName') + " " + this.get('lastName'); + return `${this.get('firstName')} ${this.get('lastName')}`; }), }); diff --git a/app/models/cloudinary_url.js b/app/models/cloudinary_url.js index b73a7d322..10d8def84 100644 --- a/app/models/cloudinary_url.js +++ b/app/models/cloudinary_url.js @@ -17,13 +17,15 @@ export default Model.extend({ generateUrl: function(width, height, crop) { var id = this.get('cloudinaryId') || "1438323573/default/test_image.jpg"; - var angle = this.get('angle') || 0; + if (!id || id.indexOf("/") === -1) { return null; } + var version = id.split("/")[0]; var filename = id.substring(id.indexOf("/") + 1); var options = this.getOptions(version, height, width, crop, id); + var angle = this.get('angle'); if(angle) { options["angle"] = angle; } return Ember.$.cloudinary.url(filename, options); } diff --git a/app/models/code.js b/app/models/code.js index 73389d9ba..6c45d346f 100644 --- a/app/models/code.js +++ b/app/models/code.js @@ -30,13 +30,7 @@ export default Model.extend({ _getPackages: function(model, packageNames) { var array = (packageNames || "").split(","); - var packages = []; var allPackageTypes = model.store.peekAll("code"); - array.forEach(function(type) { - allPackageTypes.filter(function(pkg) { - return pkg.get("code") === type ? packages.push(pkg) : ""; - }); - }); - return packages; + return allPackageTypes.filter((pkg) => array.indexOf(pkg.get("code")) > -1); } }); diff --git a/app/models/designation.js b/app/models/designation.js index 8ad785842..359ffc193 100644 --- a/app/models/designation.js +++ b/app/models/designation.js @@ -63,28 +63,18 @@ export default Model.extend({ } ), - clientIdNumber: Ember.computed("beneficiary.identityNumber", function() { - return this.get("beneficiary.identityNumber"); - }), - - clientName: Ember.computed("beneficiary.fullName", function() { - return this.get("beneficiary.fullName"); - }), - - clientPhone: Ember.computed("beneficiary.phoneNumber", function() { - return this.get("beneficiary.phoneNumber"); - }), + clientIdNumber: Ember.computed.alias("beneficiary.identityNumber"), + clientName: Ember.computed.alias("beneficiary.fullName"), + clientPhone: Ember.computed.alias("beneficiary.phoneNumber"), isEditAllowed: Ember.computed("state", function() { return !(this.get("isCancelled") || this.get("isClosed")); }), isLocalOrder: Ember.computed("detailType", function() { - return ( - this.get("detailType") === "LocalOrder" || - this.get("detailType") === "StockitLocalOrder" - ); + return ["LocalOrder", "StockitLocalOrder"].indexOf(this.get("detailType")) > -1; }), + isGoodCityOrder: Ember.computed.equal("detailType", "GoodCity"), isAppointment: Ember.computed("bookingType", function() { diff --git a/app/models/item.js b/app/models/item.js index 191953733..5db955acc 100644 --- a/app/models/item.js +++ b/app/models/item.js @@ -55,9 +55,8 @@ export default cloudinaryUrl.extend({ } ), - available_qty: Ember.computed("quantity", function() { - return this.get("quantity"); - }), + available_qty: Ember.computed.alias("quantity"), + availableQty: Ember.computed.alias("quantity"), thumbImageUrl: Ember.computed( "favouriteImage.{angle,cloudinaryId}", @@ -252,7 +251,7 @@ export default cloudinaryUrl.extend({ dispatchedOrdersPackages.forEach(record => { totalDispatchedQty += parseInt(record.get("quantity"), 10); }); - return totalDispatchedQty === received_quantity ? true : false; + return totalDispatchedQty === received_quantity; } ), @@ -268,7 +267,7 @@ export default cloudinaryUrl.extend({ designatedOrdersPackages.forEach(record => { totalDesignatedQty += parseInt(record.get("quantity"), 10); }); - return totalDesignatedQty === received_quantity ? true : false; + return totalDesignatedQty === received_quantity; } ), @@ -285,10 +284,6 @@ export default cloudinaryUrl.extend({ } ), - availableQty: Ember.computed("quantity", function() { - return this.get("quantity"); - }), - minSetQty: Ember.computed("setItem.items", function() { if (this.get("isSet") && this.get("designateFullSet")) { var setItems = this.get("setItem.items"); diff --git a/app/models/orders_package.js b/app/models/orders_package.js index 8260e6faf..ff81a29ea 100644 --- a/app/models/orders_package.js +++ b/app/models/orders_package.js @@ -20,9 +20,8 @@ export default Model.extend({ isDesignated: Ember.computed.equal("state", "designated"), isCancelled: Ember.computed.equal("state", "cancelled"), - availableQty: Ember.computed("quantity", function() { - return this.get('quantity'); - }), + availableQty: Ember.computed.alias("quantity"), + isSingleQuantity: Ember.computed.equal('quantity', 1), qtyToModify: Ember.computed("quantity", "item.quantity", function() { return this.get('quantity') + this.get("item.quantity"); @@ -32,7 +31,4 @@ export default Model.extend({ return this.get('designation.code'); }), - isSingleQuantity: Ember.computed('quantity', function(){ - return this.get('quantity') === 1; - }) }); diff --git a/app/models/package_type.js b/app/models/package_type.js index 8c56a0791..2d34a4359 100644 --- a/app/models/package_type.js +++ b/app/models/package_type.js @@ -32,7 +32,8 @@ export default Model.extend({ }), packageCategories: Ember.computed('code', "_packageCategories.[]", function(){ - return this.get("_packageCategories").filter(p => p.get("packageTypeCodes") && p.get("packageTypeCodes").indexOf(this.get("code")) > -1); + var code = this.get('code'); + return this.get("_packageCategories").filter(p => (p.get("packageTypeCodes") || []).indexOf(code) > -1); }), allPackageCategories: Ember.computed('code', "_packageCategories.[]", function(){ diff --git a/app/models/set_item.js b/app/models/set_item.js index d23e8adb1..70d5ab1b1 100644 --- a/app/models/set_item.js +++ b/app/models/set_item.js @@ -41,30 +41,24 @@ export default Model.extend({ shareSingleDesignation: Ember.computed("items.@each.orderCode", "allDesignated", function() { if(this.get("allDesignated")) { return this.get("items").map(a => a.get('orderCode')).uniq().length === 1; - } else { - return false; } + return false; }), hasZeroQty: Ember.computed("items.@each.ordersPackages", function() { - var zeroQty = true; - this.get("items").forEach(record => { - if(record.get("quantity") === 0) { - zeroQty = false; - } - }); - return zeroQty; + var zeroQtyItem = this.get("items").find(record => record.get("quantity") === 0); + return !Boolean(zeroQtyItem); }), hasSingleDesignation: Ember.computed("items.@each.ordersPackages", function() { - var lessThenOneDesignation = true; + var lessThanOneDesignation = true; this.get("items").forEach(record => { var designatedOrderPackages = record.get("ordersPackages").filterBy("state", "designated"); if(designatedOrderPackages.get("length") > 1 || designatedOrderPackages.get("length") === 0) { - lessThenOneDesignation = false; + lessThanOneDesignation = false; } }); - return lessThenOneDesignation; + return lessThanOneDesignation; }), designatedAndDispatchedOrdersPackages: Ember.computed("items.@each.ordersPackages", function() { @@ -81,34 +75,28 @@ export default Model.extend({ }), setItemOrdersPackages: Ember.computed("items.@each.ordersPackages", function() { - var designatedAndDispatchedPackages = []; - this.get("items").forEach(record => { - var orderPackages = record.get("ordersPackages").filterBy("quantity"); - orderPackages.forEach(record => { - if(record && record.get("state") !== "cancelled") { - designatedAndDispatchedPackages.push(record); - } - }); - }); - return designatedAndDispatchedPackages.get("length"); + return this.get('designatedAndDispatchedOrdersPackages.length'); }), hasSameSingleDesignation: Ember.computed("items.@each.ordersPackages", function() { var sameSingleDesignation = true; var designatedPackages = []; + this.get("items").forEach(record => { var designatedOrderPackages = record.get("ordersPackages").filterBy("state", "designated"); if(designatedOrderPackages.get("length") === 1) { designatedPackages.push(designatedOrderPackages[0].get("designationId")); } }); + designatedPackages.forEach(record => { if(record !== designatedPackages[0]) { sameSingleDesignation = false; } }); + this.set("designatedSetItemOrderPackages", designatedPackages); - return (designatedPackages.get("length") === this.get("items.length") && sameSingleDesignation) ? true : false; + return Boolean((designatedPackages.get("length") === this.get("items.length") && sameSingleDesignation)); }), canBeMoved: Ember.computed('items.@each.hasBoxPallet', function() { diff --git a/app/models/user_profile.js b/app/models/user_profile.js index 07d2df8c7..6ac3b315c 100644 --- a/app/models/user_profile.js +++ b/app/models/user_profile.js @@ -12,11 +12,7 @@ export default Addressable.extend({ userRoles: DS.hasMany('userRoles', { async: false }), roles: Ember.computed('userRoles.[]', function(){ - var roles = []; - this.get('userRoles').forEach(userRole => { - roles.push(userRole.get('role')); - }); - return roles; + return this.get('userRoles').map(userRole => userRole.get('role')); }), roleNames: Ember.computed('roles', function(){ diff --git a/tests/unit/model/set_item-test.js b/tests/unit/model/set_item-test.js index c3ecedfbd..6b515c1db 100644 --- a/tests/unit/model/set_item-test.js +++ b/tests/unit/model/set_item-test.js @@ -241,6 +241,6 @@ test('designations: returns designated ordersPackages', function(assert){ designation: designation }); model.get('designatedSetItemOrderPackages').pushObject([ordersPackage]); }); - + assert.equal(model.get('designations.length'), 1); }); From 15724d07562d8aecc67233d5a96f6aebba4855f2 Mon Sep 17 00:00:00 2001 From: swatijadhav Date: Fri, 27 Sep 2019 11:17:48 +0530 Subject: [PATCH 08/54] refactor route files --- app/routes/application.js | 34 ++++++++----------- app/routes/index.js | 12 +++---- app/routes/item_filters.js | 2 +- app/routes/items/detail.js | 1 + app/routes/items/index.js | 3 ++ app/routes/items/modify_designation.js | 2 ++ app/routes/items/new.js | 1 + app/routes/items/partial_designate.js | 1 + app/routes/items/partial_dispatch.js | 10 +++--- app/routes/items/partial_move.js | 1 + app/routes/items/partial_undesignate.js | 11 ++++--- app/routes/items/search_location.js | 3 +- app/routes/items/search_order.js | 36 +++++++-------------- app/routes/order/appointment_details.js | 4 ++- app/routes/order/goods_details.js | 7 ++-- app/routes/order/order_user_organisation.js | 4 ++- app/routes/order/search_users.js | 1 + app/routes/orders/get_order.js | 3 +- app/routes/organisation.js | 7 ++-- app/routes/select_location.js | 2 +- 20 files changed, 74 insertions(+), 71 deletions(-) diff --git a/app/routes/application.js b/app/routes/application.js index 8e77b054c..676294159 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -17,10 +17,8 @@ export default Ember.Route.extend(preloadDataMixin, { _loadDataStore: function() { return this.preloadData() .catch(error => { - if ( - error.status === 0 || - (error.errors && error.errors[0].status === "0") - ) { + let isZeroStatus = error.status === 0 || (error.errors && error.errors[0].status === "0"); + if (isZeroStatus) { this.transitionTo("offline"); } else { this.handleError(error); @@ -37,26 +35,19 @@ export default Ember.Route.extend(preloadDataMixin, { init() { var _this = this; var storageHandler = function(object) { - var currentPath = window.location.href; - var authToken = window.localStorage.getItem("authToken"); - if ( - !authToken && - !( - currentPath.indexOf("login") >= 0 || - currentPath.indexOf("authenticate") >= 0 - ) - ) { + let currentPath = window.location.href; + let authToken = window.localStorage.getItem("authToken"); + let isLoginPath = currentPath.indexOf("login") >= 0 || currentPath.indexOf("authenticate") >= 0; + + if (!authToken && !isLoginPath) { object.session.clear(); object.store.unloadSessionData(); object.transitionTo("login"); - } else if ( - authToken && - (currentPath.indexOf("login") >= 0 || - currentPath.indexOf("authenticate") >= 0) - ) { + } else if (authToken && isLoginPath) { object.transitionTo("/"); } }; + window.addEventListener( "storage", function() { @@ -79,6 +70,7 @@ export default Ember.Route.extend(preloadDataMixin, { showErrorPopup(reason) { this.get("logger").error(reason); + if (!this.get("isErrPopUpAlreadyShown")) { this.set("isErrPopUpAlreadyShown", true); this.get("messageBox").alert(this.getErrorMessage(reason), () => { @@ -89,6 +81,7 @@ export default Ember.Route.extend(preloadDataMixin, { showItemIsNotAvailable() { this.set("isItemUnavailable", true); + if (this.get("target") && this.get("target").currentPath !== "index") { this.get("messageBox").alert("This item is not available.", () => { this.set("isItemUnavailable", false); @@ -111,9 +104,10 @@ export default Ember.Route.extend(preloadDataMixin, { } catch (e) { this.get("messageBox").alert(this.get("i18n").t("QuotaExceededError")); } - localStorage.removeItem("test"); + localStorage.removeItem("test"); var language; + if (transition.queryParams.ln) { language = transition.queryParams.ln === "zh-tw" ? "zh-tw" : "en"; } @@ -145,7 +139,7 @@ export default Ember.Route.extend(preloadDataMixin, { handleError: function(reason) { try { var status; - // let hasPopup = Ember.$('.reveal-modal:visible').length > 0; + try { status = parseInt(reason.errors[0].status, 10); } catch (err) { diff --git a/app/routes/index.js b/app/routes/index.js index c54fe57eb..9e05d4c1c 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -5,15 +5,15 @@ import AjaxPromise from 'stock/utils/ajax-promise'; export default SessionRoute.extend({ model() { let canViewDashboard = this.get('session.currentUser.canViewDashboard'); - var recentlyUsedDesignations = this.get('store').query('designation', { recently_used: true }); var recentlyUsedLocations = this.get('store').query('location', { recently_used: true }); + recentlyUsedDesignations.forEach(record => { - if(record.constructor.toString() === "stock@model:designation:") { - this.store.query("orders_package", { search_by_order_id: record.get("id") - }); - } - }); + if(record.constructor.toString() === "stock@model:designation:") { + this.store.query("orders_package", { search_by_order_id: record.get("id") }); + } + }); + this.get('store').pushPayload(recentlyUsedDesignations); this.get('store').pushPayload(recentlyUsedLocations); diff --git a/app/routes/item_filters.js b/app/routes/item_filters.js index 5dd0d7a3d..b31cfd3ea 100644 --- a/app/routes/item_filters.js +++ b/app/routes/item_filters.js @@ -4,7 +4,7 @@ export default AuthorizeRoute.extend({ model() { var recentlyUsedLocations = this.store.peekAll('location').filterBy('recentlyUsedAt'); - return recentlyUsedLocations.length !== 0 ? recentlyUsedLocations :this.store.query('location', { recently_used: true }); + return recentlyUsedLocations.length ? recentlyUsedLocations : this.store.query('location', { recently_used: true }); }, deactivate(){ diff --git a/app/routes/items/detail.js b/app/routes/items/detail.js index d188d171d..b5c695f8b 100644 --- a/app/routes/items/detail.js +++ b/app/routes/items/detail.js @@ -46,6 +46,7 @@ export default AuthorizeRoute.extend({ var previousRoutes = this.router.router.currentHandlerInfos; var previousRoute = previousRoutes && previousRoutes.pop(); var path = "items.index"; + if (previousRoute) { var routeName = previousRoute.name; if (routeName.indexOf("items") === 0) { diff --git a/app/routes/items/index.js b/app/routes/items/index.js index 0dccb9bab..eaa73212c 100644 --- a/app/routes/items/index.js +++ b/app/routes/items/index.js @@ -9,6 +9,7 @@ export default AuthorizeRoute.extend({ searchInput: "", locationFilterChanged: false }, + designateFullSet: Ember.computed.localStorage(), partial_qnty: Ember.computed.localStorage(), @@ -56,9 +57,11 @@ export default AuthorizeRoute.extend({ const { hasModifiedFilters } = model; controller.applyFilter(); + if (hasModifiedFilters) { controller.onFilterChange(); } + controller.set("itemSetId", this.paramsFor("items.index").itemSetId); controller.on(); }, diff --git a/app/routes/items/modify_designation.js b/app/routes/items/modify_designation.js index 9d9a4c0b9..e70f2de02 100644 --- a/app/routes/items/modify_designation.js +++ b/app/routes/items/modify_designation.js @@ -12,6 +12,7 @@ export default AuthorizeRoute.extend({ this.set("ordersPackageId", params.ordersPackageId); var item = this.store.peekRecord("item", params.item_id); var ordersPackage = this.store.peekRecord("orders_package", params.ordersPackageId); + return Ember.RSVP.hash({ item: item || this.store.findRecord('item', params.item_id), ordersPackage: ordersPackage || this.store.findRecord("orders_package", params.ordersPackageId) @@ -21,6 +22,7 @@ export default AuthorizeRoute.extend({ afterModel(model) { var ordersPkg = model.ordersPackage; var designationId = ordersPkg.get('designationId'); + if(designationId) { var designation = this.store.peekRecord("designation", designationId) || this.store.findRecord("designation", designationId); return designation; diff --git a/app/routes/items/new.js b/app/routes/items/new.js index 6578fbfcb..f46d431cf 100644 --- a/app/routes/items/new.js +++ b/app/routes/items/new.js @@ -25,6 +25,7 @@ export default AuthorizeRoute.extend({ beforeModel() { this._super(...arguments); var searchCodePreviousRoute = this.get("isSearchCodePreviousRoute"); + if (searchCodePreviousRoute) { var newItemRequest = searchCodePreviousRoute ? true : false; this.set("newItemRequest", newItemRequest); diff --git a/app/routes/items/partial_designate.js b/app/routes/items/partial_designate.js index 51a2690c1..83fd382d3 100644 --- a/app/routes/items/partial_designate.js +++ b/app/routes/items/partial_designate.js @@ -12,6 +12,7 @@ export default AuthorizeRoute.extend({ model(params) { getOwner(this).lookup('controller:items.search_order').set('notPartialRoute', false); var recentlyUsedDesignations = this.get('store').query('designation', { shallow: true, recently_used: true }); + recentlyUsedDesignations.forEach(record => { if(record.constructor.toString() === "stock@model:designation:") { this.store.query("orders_package", { search_by_order_id: record.get("id") diff --git a/app/routes/items/partial_dispatch.js b/app/routes/items/partial_dispatch.js index a43f102ba..05a1bc3dd 100644 --- a/app/routes/items/partial_dispatch.js +++ b/app/routes/items/partial_dispatch.js @@ -16,13 +16,15 @@ export default AuthorizeRoute.extend({ if(previousRoute) { var routeName = previousRoute.name; + var backLinkPath = "items.detail"; + if(routeName === "items"){ - this.set("partialDispatchBackLinkpath", "items.index"); + backLinkPath = "items.index"; } else if(routeName === "items.partial_undesignate" || routeName === 'orders.detail') { - this.set("partialDispatchBackLinkpath", routeName); - } else { - this.set("partialDispatchBackLinkpath", "items.detail"); + backLinkPath = routeName; } + + this.set("partialDispatchBackLinkpath", backLinkPath); } }, diff --git a/app/routes/items/partial_move.js b/app/routes/items/partial_move.js index 6b3b41605..4af33feb3 100644 --- a/app/routes/items/partial_move.js +++ b/app/routes/items/partial_move.js @@ -4,6 +4,7 @@ export default AuthorizeRoute.extend({ model(params){ return this.store.peekRecord("item", params.item_id) || this.store.findRecord('item', params.item_id); }, + setupController(controller, model){ this._super(controller, model); controller.set('isEditing', false); diff --git a/app/routes/items/partial_undesignate.js b/app/routes/items/partial_undesignate.js index c5f243ad2..960e4043a 100644 --- a/app/routes/items/partial_undesignate.js +++ b/app/routes/items/partial_undesignate.js @@ -11,11 +11,13 @@ export default AuthorizeRoute.extend({ if(previousRoute) { var routeName = previousRoute.name; + var backLinkPath = "items.detail"; + if(routeName === "items"){ - this.set("partialUndesignateBackLinkpath", "items.index"); - } else { - this.set("partialUndesignateBackLinkpath", "items.detail"); + backLinkPath = "items.index"; } + + this.set("partialUndesignateBackLinkpath", backLinkPath); } }, @@ -25,7 +27,8 @@ export default AuthorizeRoute.extend({ }, afterModel(model) { - var designation = null; + var designation; + if(model) { model.get('ordersPackages').forEach( orderPackage => { var orderId = orderPackage.get('designationId'); diff --git a/app/routes/items/search_location.js b/app/routes/items/search_location.js index dd6413353..ed5556bcd 100644 --- a/app/routes/items/search_location.js +++ b/app/routes/items/search_location.js @@ -21,8 +21,10 @@ export default AuthorizeRoute.extend({ if(previousRoute.name === "orders.active_items") { this.set("orderId", previousRoute.params.order_id); } + this.set('itemPreviousRoute', previousRoute.name); var routeName = previousRoute.name; + if(routeName.indexOf("active_items")){ path = routeName; } @@ -51,4 +53,3 @@ export default AuthorizeRoute.extend({ controller.set('selectedLocation', null); } }); - diff --git a/app/routes/items/search_order.js b/app/routes/items/search_order.js index 0b89cc309..e1913992b 100644 --- a/app/routes/items/search_order.js +++ b/app/routes/items/search_order.js @@ -27,23 +27,11 @@ export default AuthorizeRoute.extend({ var path = "items.index"; - if (previousRoute) { - var routeName = previousRoute.name; - if (routeName.indexOf("detail")) { - path = routeName; - } - if (routeName === "items.partial_designate") { - path = "items.index"; - this.set("partialDesignatePath", true); - } else { - this.set("partialDesignatePath", false); - } - } - if (parseInt(window.localStorage.getItem("partial_qnty"), 10)) { - this.set("partialDesignatePath", true); - } else { - this.set("partialDesignatePath", false); + if (previousRoute && routeName.indexOf("detail")) { + path = previousRoute.name; } + + this.set("partialDesignatePath", Boolean(parseInt(window.localStorage.getItem("partial_qnty"), 10))); this.set("itemDesignateBackLinkPath", path); }, @@ -52,6 +40,7 @@ export default AuthorizeRoute.extend({ var recentlyUsedDesignations = this.store .peekAll("designation") .filterBy("recentlyUsedAt"); + recentlyUsedDesignations.forEach(record => { if (record.constructor.toString() === "stock@model:designation:") { this.store.query("orders_package", { @@ -63,7 +52,7 @@ export default AuthorizeRoute.extend({ return Ember.RSVP.hash({ item: item || this.store.findRecord("item", params.item_id), designations: - recentlyUsedDesignations.get("length") !== 0 + recentlyUsedDesignations.get("length") ? recentlyUsedDesignations : this.get("store").query("designation", { shallow: true, @@ -74,14 +63,11 @@ export default AuthorizeRoute.extend({ setupController(controller, model) { this._super(controller, model); - if ( - !this.get("partialDesignatePath") && - !parseInt(window.localStorage.getItem("partial_qnty"), 10) - ) { - controller.set("notPartialRoute", true); - } else { - controller.set("notPartialRoute", false); - } + + let isNotPartialRoute = !this.get("partialDesignatePath") && + !parseInt(window.localStorage.getItem("partial_qnty"), 10); + + controller.set("notPartialRoute", isNotPartialRoute); controller.set("searchText", ""); controller.set("backLinkPath", this.get("itemDesignateBackLinkPath")); }, diff --git a/app/routes/order/appointment_details.js b/app/routes/order/appointment_details.js index 0cf600d01..00e3a5db3 100644 --- a/app/routes/order/appointment_details.js +++ b/app/routes/order/appointment_details.js @@ -25,15 +25,17 @@ export default orderUserOrganisation.extend({ let orderTransport = model.orderTransport; let availableDatesAndTime = model.availableDatesAndtime; let slots = null; + if (orderTransport){ selectedId = orderTransport.get('transportType'); selectedTime = orderTransport.get('timeslot'); selectedDate = moment.tz(orderTransport.get("scheduledAt"), 'Asia/Hong_Kong'); if(selectedDate) { slots = availableDatesAndTime.appointment_calendar_dates.filter( date => date.date === selectedDate.format('YYYY-MM-DD'))[0].slots; - selectedSlot = slots.filter(slot => slot.timestamp.indexOf(orderTransport.get("timeslot")) >= 0)[0]; + selectedSlot = slots.find(slot => slot.timestamp.indexOf(orderTransport.get("timeslot")) >= 0); } } + controller.set('selectedId', selectedId); controller.set('selectedTimeId', selectedTime); controller.set('available_dates', availableDatesAndTime); diff --git a/app/routes/order/goods_details.js b/app/routes/order/goods_details.js index e1fdac5d9..0b1e0486c 100644 --- a/app/routes/order/goods_details.js +++ b/app/routes/order/goods_details.js @@ -13,11 +13,12 @@ export default orderUserOrganisation.extend({ .then((data) => { this.set("orderId", data["designation"]["id"]); store.pushPayload(data); + if(!data['goodcity_requests'].length){ return new AjaxPromise("/goodcity_requests", "POST", this.get('session.authToken'), { goodcity_request: goodcityRequestParams }) - .then(data => { - store.pushPayload(data); - }); + .then(data => { + store.pushPayload(data); + }); } }); }, diff --git a/app/routes/order/order_user_organisation.js b/app/routes/order/order_user_organisation.js index 5e828bda6..76bdd1fbb 100644 --- a/app/routes/order/order_user_organisation.js +++ b/app/routes/order/order_user_organisation.js @@ -20,6 +20,8 @@ export default AuthorizeRoute.extend({ /* jshint ignore:end */ afterModel() { - Ember.$('body').animate({scrollTop: 0}); //https://github.com/dollarshaveclub/ember-router-scroll. Read this link for nested route issue for not scrolling at top of the page + // Refer: for nested route issue for not scrolling at top of the page + // https://github.com/dollarshaveclub/ember-router-scroll. + Ember.$('body').animate({scrollTop: 0}); } }); diff --git a/app/routes/order/search_users.js b/app/routes/order/search_users.js index 70f89c36f..1a2bab8a1 100644 --- a/app/routes/order/search_users.js +++ b/app/routes/order/search_users.js @@ -22,6 +22,7 @@ export default AuthorizeRoute.extend({ this._super(controller, model); this.get("store").pushPayload(model.recentUsers); let users = []; + model.recentUsers.users.forEach(user => { let userObject = this.store.peekRecord("user", user.id); user.organisations_users_ids.forEach(org_user_id => { diff --git a/app/routes/orders/get_order.js b/app/routes/orders/get_order.js index 8e05a9ede..2c4ce4160 100644 --- a/app/routes/orders/get_order.js +++ b/app/routes/orders/get_order.js @@ -3,6 +3,7 @@ import AuthorizeRoute from './../authorize'; export default AuthorizeRoute.extend({ model(params) { - return (this.store.peekRecord("designation", params.order_id, { reload: true }) || this.store.findRecord("designation", params.order_id, { reload: true })); + return (this.store.peekRecord("designation", params.order_id, { reload: true }) || + this.store.findRecord("designation", params.order_id, { reload: true })); } }); diff --git a/app/routes/organisation.js b/app/routes/organisation.js index 70d6a2198..9a575cbb3 100644 --- a/app/routes/organisation.js +++ b/app/routes/organisation.js @@ -2,12 +2,13 @@ import AuthorizeRoute from './authorize'; export default AuthorizeRoute.extend({ model(params) { - return this.store.peekRecord("gc_organisation", params.organisation_id) || this.store.findRecord( - "gc_organisation", params.organisation_id, { reload: true }); + return this.store.peekRecord("gc_organisation", params.organisation_id) || + this.store.findRecord("gc_organisation", params.organisation_id, { reload: true }); }, setupController(controller, model){ this._super(controller, model); - controller.set('gcOrganisationUsers', this.store.peekAll('organisationsUser').filterBy('organisationId', parseInt(model.id))); + let gcOrganisationUsers = this.store.peekAll('organisationsUser').filterBy('organisationId', parseInt(model.id)); + controller.set('gcOrganisationUsers', gcOrganisationUsers); } }); diff --git a/app/routes/select_location.js b/app/routes/select_location.js index a56a4d535..a54edbb00 100644 --- a/app/routes/select_location.js +++ b/app/routes/select_location.js @@ -7,7 +7,7 @@ export default AuthorizeRoute.extend({ model() { this.set("isSelectLocationPreviousRoute", true); var recentlyUsedLocations = this.store.peekAll('location').filterBy('recentlyUsedAt'); - return recentlyUsedLocations.length !== 0 ? recentlyUsedLocations :this.store.query('location', { recently_used: true }); + return recentlyUsedLocations.length ? recentlyUsedLocations : this.store.query('location', { recently_used: true }); }, setupController(controller, model){ From 8f614043419522973af8e65b588db61cd0ad5650 Mon Sep 17 00:00:00 2001 From: Namrata Ukirde Date: Tue, 1 Oct 2019 20:26:00 +0530 Subject: [PATCH 09/54] Create PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..b0e866268 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +### Ticket Link: + +NOTE: Specify ticket link + +### What does this PR do? + +FEATURE: Specify feature name + +BUG: Specify bug + +NOTE: Give a description of what this PR does. If it fixes any bug, specify cause of an issue and approach/solution added to fix that issue. + + +### Impacted Areas + +NOTE: List any impacted areas (e.g. Dashboard > My Active Offers > scheduled ) + + +### Screenshots + +NOTE: Attach screeenshots if PR contains any UI changes + +### Mockup Link + +Note: Specify mockup link + +### Linked PR's: + +NOTE: If these changes are related to some existing PR or fixes any issue from existing PR changes, specify PR links. + +### Linked Slack conversation: + +NOTE: IF there is any conversation happened for current changes in any of the slack channel, mention the slack message link. + +### Any Open question(s) or challenge(s): From cc41d5554998c4a5734c6c3664c1d528c61d7156 Mon Sep 17 00:00:00 2001 From: swatijadhav Date: Tue, 1 Oct 2019 16:51:53 +0530 Subject: [PATCH 10/54] fixed code-duplication --- app/models/item.js | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/app/models/item.js b/app/models/item.js index 5db955acc..e81470a01 100644 --- a/app/models/item.js +++ b/app/models/item.js @@ -169,13 +169,7 @@ export default cloudinaryUrl.extend({ desinatedAndDisaptchedItemPackages: Ember.computed( "ordersPackages.[]", function() { - var orderPackages = this.get("ordersPackages").filterBy("quantity"); - orderPackages.forEach(record => { - if (record && record.get("state") === "cancelled") { - orderPackages.removeObject(record); - } - }); - return orderPackages.get("length"); + return this.get("ordersPackagesWithStateDesignatedAndDispatched.length"); } ), @@ -242,32 +236,22 @@ export default cloudinaryUrl.extend({ hasAllPackagesDispatched: Ember.computed( "ordersPackages.@each.state", function() { - var received_quantity = this.get("receivedQuantity"); - var totalDispatchedQty = 0; - var dispatchedOrdersPackages = this.get("ordersPackages").filterBy( - "state", - "dispatched" - ); - dispatchedOrdersPackages.forEach(record => { - totalDispatchedQty += parseInt(record.get("quantity"), 10); - }); - return totalDispatchedQty === received_quantity; + return this.packagesByState("dispatched"); } ), + packagesByState(state) { + var received_quantity = this.get("receivedQuantity"); + var ordersPackages = this.get("ordersPackages").filterBy("state", state); + var totalQty = ordersPackages.reduce((qty, record) => qty + parseInt(record.get("quantity"), 10), 0); + + return totalQty === received_quantity; + }, + hasAllPackagesDesignated: Ember.computed( "ordersPackages.@each.state", function() { - var received_quantity = this.get("receivedQuantity"); - var totalDesignatedQty = 0; - var designatedOrdersPackages = this.get("ordersPackages").filterBy( - "state", - "designated" - ); - designatedOrdersPackages.forEach(record => { - totalDesignatedQty += parseInt(record.get("quantity"), 10); - }); - return totalDesignatedQty === received_quantity; + return this.packagesByState("designated"); } ), From cf8e9847e7ed1e8c9f68ccaf4864e4406f52d091 Mon Sep 17 00:00:00 2001 From: swatijadhav Date: Thu, 3 Oct 2019 14:57:53 +0530 Subject: [PATCH 11/54] removed unused computed property --- app/models/item.js | 1 - tests/unit/model/item-test.js | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/app/models/item.js b/app/models/item.js index e81470a01..a1da4c728 100644 --- a/app/models/item.js +++ b/app/models/item.js @@ -55,7 +55,6 @@ export default cloudinaryUrl.extend({ } ), - available_qty: Ember.computed.alias("quantity"), availableQty: Ember.computed.alias("quantity"), thumbImageUrl: Ember.computed( diff --git a/tests/unit/model/item-test.js b/tests/unit/model/item-test.js index bfbbaaff8..8378499bd 100644 --- a/tests/unit/model/item-test.js +++ b/tests/unit/model/item-test.js @@ -107,16 +107,6 @@ test("Relationships with other models", function(assert) { assert.equal(relationshipWithImage.kind, "hasMany"); }); -test("check available_qty computed property", function(assert) { - var model = this.subject(); - - Ember.run(function() { - model.set("available_qty", 2); - }); - - assert.equal(model.get("available_qty"), 2); -}); - test("check validUndispatchedLocations computed property", function(assert) { assert.expect(2); var model, @@ -690,12 +680,6 @@ test("check imageUrlList computed property", function(assert) { ); }); -test("available_qty: Returns available qty", function(assert) { - assert.expect(1); - var model = this.subject({ id: 1, quantity: 2 }); - assert.equal(model.get("available_qty"), 2); -}); - test("setImages: returns associated items imageUrlList", function(assert) { var store, item, setItem; From 3ffd5f4dcc3abaecbad841e11d4a4b1ed1167867 Mon Sep 17 00:00:00 2001 From: AbulAsar Date: Thu, 3 Oct 2019 17:57:16 +0530 Subject: [PATCH 12/54] fixed rubyzip vulenrabilities --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index b17c19882..49438cdf2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,7 +171,7 @@ GEM retriable (3.1.2) rouge (2.0.7) ruby-macho (1.4.0) - rubyzip (1.2.3) + rubyzip (1.3.0) security (0.1.3) signet (0.11.0) addressable (~> 2.3) From f973a671fb8113b455473bd54cec2e4c18b1f2c6 Mon Sep 17 00:00:00 2001 From: AbulAsar Date: Wed, 18 Sep 2019 16:41:31 +0530 Subject: [PATCH 13/54] added infinite scroll for items search page --- app/controllers/items/index.js | 110 ++++++++++++++++++++++----------- app/routes/items/index.js | 8 --- app/templates/items/index.hbs | 20 +++--- 3 files changed, 82 insertions(+), 56 deletions(-) diff --git a/app/controllers/items/index.js b/app/controllers/items/index.js index bbebc27f4..0de12abe7 100644 --- a/app/controllers/items/index.js +++ b/app/controllers/items/index.js @@ -1,10 +1,9 @@ import config from "../../config/environment"; import Ember from "ember"; -import searchModule from "../search_module"; +import _ from "lodash"; -export default searchModule.extend({ +export default Ember.Controller.extend({ queryParams: ["searchInput", "itemSetId", "locationFilterChanged"], - searchInput: "", itemSetId: null, locationFilterChanged: null, isMobileApp: config.cordova.enabled, @@ -12,64 +11,101 @@ export default searchModule.extend({ displayItemOptionsList: true, searchModelName: "item", minSearchTextLength: 2, + searchedText: "", + filterService: Ember.inject.service(), + utilityMethods: Ember.inject.service(), requestOptions: { withInventoryNumber: "true" }, + displayResults: false, + on() { - this.get("filterService").on("change", this, this.onFilterChange); + this.showResults(); // Upon opening the page, we populate with results + this.get("filterService").on("change", this, this.reloadResults); }, off() { - this.get("filterService").off("change", this, this.onFilterChange); + this.get("filterService").off("change", this, this.reloadResults); + }, + + onSearchTextChange: Ember.observer("searchText", function() { + if (this.get("searchText").length > this.get("minSearchTextLength")) { + this.reloadResults(); + } + }), + + reloadResults() { + this.hideResults(); + Ember.run.debounce(this, this.showResults, 500); + }, + + hideResults() { + Ember.run(() => { + this.set("displayResults", false); + }); + }, + + showResults() { + Ember.run(() => { + this.set("displayResults", true); + }); + }, + + getSearchQuery() { + return { + searchText: this.get("searchText"), + shallow: true + }; }, - createFilterParams() { + getPaginationQuery(pageNo) { + return { + per_page: 25, + page: pageNo + }; + }, + + trimQuery(query) { + // Remove any undefined values + return _.pickBy(query, _.identity); + }, + + getFilterQuery() { let filterService = this.get("filterService"); let utilities = this.get("utilityMethods"); let itemStateFilters = filterService.get("itemStateFilters"); let itemlocationFilter = filterService.get("itemLocationFilters"); return { - perPage: 25, - startingPage: 1, - modelPath: "filteredResults", stockRequest: true, state: utilities.stringifyArray(itemStateFilters) || "received", location: itemlocationFilter }; }, - applyFilter(opts = {}) { - const { force = false } = opts; - var searchText = this.get("searchText"); - let UNLOAD_MODELS = ["designation", "item", "location", "code"]; - - if (force || searchText.length) { - this.set("isLoading", true); - this.set("hasNoResults", false); - if (this.get("unloadAll")) { - UNLOAD_MODELS.forEach(model => this.store.unloadAll(model)); - } - this.infinityModel( - this.get("searchModelName"), - this.createFilterParams(), - this.buildQueryParamMap() - ) - .then(data => { - if (force || this.get("searchText") === data.meta.search) { - this.set("filteredResults", data); - this.set("hasNoResults", data.get("length") === 0); - } - }) - .finally(() => this.set("isLoading", false)); - } - this.set("filteredResults", []); - }, - onItemSetIdChange: Ember.observer("itemSetId", function() { // wait before applying the filter if (this.get("itemSetId")) { Ember.run.debounce(this, this.applyFilter, 0); } - }) + }), + + actions: { + loadMoreItems(pageNo) { + const params = this.trimQuery( + _.merge( + {}, + this.getFilterQuery(), + this.getSearchQuery(), + this.getPaginationQuery(pageNo) + ) + ); + + return this.get("store") + .query("item", params) + .then(results => { + return results; + }); + } + } }); diff --git a/app/routes/items/index.js b/app/routes/items/index.js index eaa73212c..9282377a1 100644 --- a/app/routes/items/index.js +++ b/app/routes/items/index.js @@ -54,14 +54,6 @@ export default AuthorizeRoute.extend({ this._super(controller, model); this.set("designateFullSet", false); this.set("partial_qnty", 0); - - const { hasModifiedFilters } = model; - controller.applyFilter(); - - if (hasModifiedFilters) { - controller.onFilterChange(); - } - controller.set("itemSetId", this.paramsFor("items.index").itemSetId); controller.on(); }, diff --git a/app/templates/items/index.hbs b/app/templates/items/index.hbs index 15d898e56..425600a7c 100644 --- a/app/templates/items/index.hbs +++ b/app/templates/items/index.hbs @@ -28,17 +28,15 @@ {{/if}} - {{#if filteredResults}} -
    - {{#each filteredResults as |item|}} - {{partial 'orders/item_block'}} - {{/each}} -
- - {{#infinity-loader}} - {{/infinity-loader}} - - {{else if hasNoResults}} + {{#if displayResults}} + {{#infinite-list height="75vh" loadMore=(action "loadMoreItems") as |items| }} +
    + {{#each items as |item|}} + {{partial 'orders/item_block'}} + {{/each}} +
+ {{/infinite-list}} + {{else}}
{{t "no_search_results"}}
{{#if stockAppVersion}} From 0fdb2fd549893083a03c5977b76a7728f0a48147 Mon Sep 17 00:00:00 2001 From: AbulAsar Date: Wed, 18 Sep 2019 18:55:06 +0530 Subject: [PATCH 14/54] added infinite scroll on order items search --- app/controllers/orders/items.js | 96 +++++++++++++++++++++------------ app/templates/items/index.hbs | 6 --- app/templates/orders/items.hbs | 20 ++----- 3 files changed, 67 insertions(+), 55 deletions(-) diff --git a/app/controllers/orders/items.js b/app/controllers/orders/items.js index 07b4719a2..ceedaff50 100644 --- a/app/controllers/orders/items.js +++ b/app/controllers/orders/items.js @@ -1,49 +1,60 @@ import config from "../../config/environment"; import Ember from "ember"; -import searchModule from "../search_module"; +import _ from "lodash"; -export default searchModule.extend({ +export default Ember.Controller.extend({ queryParams: ["searchInput"], - searchInput: "", hideDetailsLink: true, orderId: Ember.computed.alias("model.id"), isMobileApp: config.cordova.enabled, autoDisplayOverlay: false, minSearchTextLength: 2, + searchText: "", + displayResults: false, - applyFilter() { - this.set("autoDisplayOverlay", false); - var searchText = this.get("searchText").trim(); - if (searchText) { - this.set("isLoading", true); - this.set("hasNoResults", false); - this.infinityModel( - "item", - { - perPage: 25, - startingPage: 1, - modelPath: "filteredResults", - stockRequest: true, - restrictMultiQuantity: true - }, - { orderId: "orderId", searchText: "searchText" } - ) - .then(data => { - if (this.get("searchText").trim() !== data.meta.search) { - return; - } - - this.set("filteredResults", data); - this.set("hasNoResults", data.get("length") === 0); - - if (data.get("length") === 1) { - Ember.run.debounce(this, this.triggerDisplayDesignateOverlay, 100); - } - }) - .finally(() => this.set("isLoading", false)); + onSearchTextChange: Ember.observer("searchText", function() { + if (this.get("searchText").length > this.get("minSearchTextLength")) { + this.showResults(); } - this.set("filteredResults", []); + }), + + getFilterQuery() { + return { + stockRequest: true, + restrictMultiQuantity: true + }; + }, + + hideResults() { + Ember.run(() => { + this.set("displayResults", false); + }); + }, + + showResults() { + Ember.run(() => { + this.set("displayResults", true); + }); + }, + + getSearchQuery() { + return { + searchText: this.get("searchText"), + shallow: true + }; + }, + + getPaginationQuery(pageNo) { + return { + per_page: 25, + page: pageNo + }; + }, + + trimQuery(query) { + // Remove any undefined values + return _.pickBy(query, _.identity); }, triggerDisplayDesignateOverlay() { @@ -51,6 +62,23 @@ export default searchModule.extend({ }, actions: { + loadMoreItems(pageNo) { + const params = this.trimQuery( + _.merge( + {}, + this.getFilterQuery(), + this.getSearchQuery(), + this.getPaginationQuery(pageNo) + ) + ); + + return this.get("store") + .query("item", params) + .then(results => { + return results; + }); + }, + displaySetItems(item) { this.set("itemSetId", item.get("itemId")); Ember.run.debounce(this, this.applyFilter, 0); diff --git a/app/templates/items/index.hbs b/app/templates/items/index.hbs index 425600a7c..370c4af36 100644 --- a/app/templates/items/index.hbs +++ b/app/templates/items/index.hbs @@ -22,12 +22,6 @@
+ +
+
+ {{fa-icon 'shopping-basket'}} + {{t 'item.related_orders' }} +
+ + {{#each model.ordersPackages as |orderPkg|}} +
+ {{goodcity/orders-package-block orderPkg=orderPkg }} +
+ {{/each}} +
+ {{goodcity/orders-search-overlay open=true}}
From 2a0a66d19d47c5365c27e5ab9a1f42afddc557af Mon Sep 17 00:00:00 2001 From: Patrick R Date: Wed, 9 Oct 2019 12:15:37 +0800 Subject: [PATCH 25/54] orders packages list && orders-search-overlay 2/2 --- .../goodcity/orders-package-block.js | 165 ++++++++++++++++++ .../goodcity/orders-search-overlay.js | 34 ++++ app/controllers/items/detail.js | 13 ++ app/controllers/orders/index.js | 70 ++++++-- app/helpers/orders-package-icon.js | 19 ++ app/helpers/toggle.js | 20 +++ app/mixins/search_resource.js | 5 +- app/routes/items/search_order.js | 23 +-- app/services/defer-service.js | 69 ++++++++ app/services/designation-service.js | 102 +++++++++++ app/services/navigation.js | 27 +++ .../templates/components/_action_drawer.scss | 25 +++ .../templates/components/_popup_overlay.scss | 15 ++ .../templates/components/_side_drawer.scss | 30 ++++ .../goodcity/_orders_package_block.scss | 45 +++++ .../goodcity/_orders_search_overlay.scss | 54 ++++++ .../items/detail/_publishing_tab.scss | 2 +- app/templates/components/action-drawer.hbs | 12 ++ .../goodcity/orders-package-block.hbs | 29 +++ .../goodcity/orders-search-overlay.hbs | 41 +++++ app/templates/components/popup-overlay.hbs | 3 + app/templates/components/side-drawer.hbs | 14 ++ app/templates/items/detail/_tabs.hbs | 2 +- .../items/detail/tabs/publishing.hbs | 3 +- 24 files changed, 796 insertions(+), 26 deletions(-) create mode 100644 app/components/goodcity/orders-package-block.js create mode 100644 app/components/goodcity/orders-search-overlay.js create mode 100644 app/helpers/orders-package-icon.js create mode 100644 app/helpers/toggle.js create mode 100644 app/services/defer-service.js create mode 100644 app/services/designation-service.js create mode 100644 app/services/navigation.js create mode 100644 app/styles/templates/components/_action_drawer.scss create mode 100644 app/styles/templates/components/_popup_overlay.scss create mode 100644 app/styles/templates/components/_side_drawer.scss create mode 100644 app/styles/templates/components/goodcity/_orders_package_block.scss create mode 100644 app/styles/templates/components/goodcity/_orders_search_overlay.scss create mode 100644 app/templates/components/action-drawer.hbs create mode 100644 app/templates/components/goodcity/orders-package-block.hbs create mode 100644 app/templates/components/goodcity/orders-search-overlay.hbs create mode 100644 app/templates/components/popup-overlay.hbs create mode 100644 app/templates/components/side-drawer.hbs diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js new file mode 100644 index 000000000..4bacbed8a --- /dev/null +++ b/app/components/goodcity/orders-package-block.js @@ -0,0 +1,165 @@ +import Ember from "ember"; +import _ from "lodash"; + +const ACTIONS_SETTINGS = { + dispatch: { + icon: "paper-plane" + }, + cancel: { + icon: "times-circle" + }, + undispatch: { + icon: "reply" + }, + redesignate: { + icon: "shopping-basket", + params: ["order_id"] + }, + edit_quantity: { + icon: "edit", + params: ["quantity"] + } +}; + +const FORCE_DISABLE = ["edit_quantity"]; + +const CANCELLED = {}; + +/** + * UI Component representing an orders_package + * + * It provides a drawer from the right side which is populated + * with the actions provided by the backend + * + * Clicking on an action will run it on the API side + */ +export default Ember.Component.extend({ + store: Ember.inject.service(), + messageBox: Ember.inject.service(), + i18n: Ember.inject.service(), + actionRunner: Ember.inject.service("designationService"), + orderSearchProps: { + state: ["submitted", "processing", "awaiting_dispatch", "dispatching"].join( + "," + ) + }, + + init() { + this._super(...arguments); + this.set("paramGetters", { + order_id: "selectOrder", + quantity: "selectQuantity" + }); + }, + + // --- PARAMS RESOLUTION + + selectOrder() { + const deferred = Ember.RSVP.defer(); + + this.set("orderSelected", order => { + deferred.resolve(order ? order.get("id") : CANCELLED); + this.set("orderSelected", _.noop); + }); + + this.set("openOrderSearch", true); + + return deferred.promise; + }, + + async selectQuantity() { + throw new Error("Not implemennted"); + }, + + async fulfillParams(actionName) { + const paramNames = _.get(ACTIONS_SETTINGS, `${actionName}.params`, []); + + let params = {}; + for (let name of paramNames) { + const getterName = this.getWithDefault(`paramGetters.${name}`, ""); + const val = await this[getterName](); + + if (val === CANCELLED) { + return CANCELLED; + } + params[name] = val; + } + return params; + }, + + // --- EXECUTION + + showSpinner() { + if (!this.loadingView) { + this.loadingView = Ember.getOwner(this) + .lookup("component:loading") + .append(); + } + }, + + hideSpinner() { + if (this.loadingView) { + this.loadingView.destroy(); + this.loadingView = null; + } + }, + + onError(reason) { + const defaultMessage = this.get("i18n").t("unexpected_error"); + const message = _.get( + reason, + "responseJSON.errors[0].message", + defaultMessage + ); + this.get("messageBox").alert(message); + }, + + async runAction(actionName) { + const params = await this.fulfillParams(actionName); + const ordersPkg = this.get("orderPkg"); + + if (params === CANCELLED) { + return; + } + + this.showSpinner(); + + try { + await this.get("actionRunner").execAction(ordersPkg, actionName, params); + this.hideSpinner(); + } catch (e) { + this.hideSpinner(); + this.onError(e); + } + }, + + // --- MENU + + labelFor(actionName) { + return this.get("i18n").t(`orders_package.actions.${actionName}`); + }, + + iconFor(actionName) { + return _.get(ACTIONS_SETTINGS, `${actionName}.icon`, []); + }, + + isDisabled(actionName) { + return FORCE_DISABLE.includes(actionName); + }, + + actionList: Ember.computed( + "orderPkg.id", + "orderPkg.allowedActions.@each", + "orderPkg.allowedActions.[]", + function() { + return this.getWithDefault("orderPkg.allowedActions", []).map( + ({ name, enabled }) => ({ + label: this.labelFor(name), + icon: this.iconFor(name), + enabled: this.isDisabled(name) ? false : enabled, + trigger: this.runAction.bind(this, name) + }) + ); + } + ) +}); diff --git a/app/components/goodcity/orders-search-overlay.js b/app/components/goodcity/orders-search-overlay.js new file mode 100644 index 000000000..b8edea560 --- /dev/null +++ b/app/components/goodcity/orders-search-overlay.js @@ -0,0 +1,34 @@ +import Ember from "ember"; +import _ from "lodash"; +import SearchMixin from "stock/mixins/search_resource"; + +export default Ember.Component.extend(SearchMixin, { + searchProps: {}, + autoLoad: true, + store: Ember.inject.service(), + perPage: 10, + + init() { + this._super(...arguments); + this.set("uuid", _.uniqueId("orders_search_overlay_")); + }, + + actions: { + cancel() { + this.send("selectOrder", null); + }, + + selectOrder(order) { + this.getWithDefault("onSelect", _.noop)(order); + this.set("open", false); + }, + + loadMoreOrders(pageNo) { + const params = this.trimQuery( + _.merge({}, this.getSearchQuery(), this.getPaginationQuery(pageNo)) + ); + + return this.get("store").query("designation", params); + } + } +}); diff --git a/app/controllers/items/detail.js b/app/controllers/items/detail.js index 8bece28eb..0ca4c88a9 100644 --- a/app/controllers/items/detail.js +++ b/app/controllers/items/detail.js @@ -111,6 +111,19 @@ export default GoodcityController.extend(singletonItemDispatchToGcOrder, { } ), + sortedOrdersPackages: Ember.computed( + "model.ordersPackages.[]", + "model.ordersPackages.@each.state", + function() { + // Cancelled orders packages are moved to the bottom + const records = this.get("model.ordersPackages"); + return [ + ...records.rejectBy("state", "cancelled"), + ...records.filterBy("state", "cancelled") + ]; + } + ), + actions: { /** * Called after a property is changed to push the updated diff --git a/app/controllers/orders/index.js b/app/controllers/orders/index.js index bc9b559d5..c7ddcaaee 100644 --- a/app/controllers/orders/index.js +++ b/app/controllers/orders/index.js @@ -1,17 +1,14 @@ import Ember from "ember"; import _ from "lodash"; import { STATE_FILTERS } from "../../services/filter-service"; -import SearchMixin from "stock/mixins/search_resource"; - -export default Ember.Controller.extend(SearchMixin, { - /** - * @type {Boolean}, expected in SearchMixin - **/ - autoLoad: true, - /** - * @type {Number}, perPage in response - **/ - perPage: 25, + +export default Ember.Controller.extend({ + minSearchTextLength: 2, + searchedText: "", + displayResults: false, + + filterService: Ember.inject.service(), + utilityMethods: Ember.inject.service(), afterSearch(designations) { if (designations && designations.get("length") > 0) { @@ -21,6 +18,38 @@ export default Ember.Controller.extend(SearchMixin, { } }, + on() { + this.showResults(); // Upon opening the page, we populate with results + this.get("filterService").on("change", this, this.reloadResults); + }, + + off() { + this.get("filterService").off("change", this, this.reloadResults); + }, + + onSearchTextChange: Ember.observer("searchText", function() { + if (this.get("searchText").length > this.get("minSearchTextLength")) { + this.reloadResults(); + } + }), + + reloadResults() { + this.hideResults(); + Ember.run.debounce(this, this.showResults, 500); + }, + + hideResults() { + Ember.run(() => { + this.set("displayResults", false); + }); + }, + + showResults() { + Ember.run(() => { + this.set("displayResults", true); + }); + }, + getFilterQuery() { const filterService = this.get("filterService"); const utils = this.get("utilityMethods"); @@ -42,6 +71,25 @@ export default Ember.Controller.extend(SearchMixin, { }; }, + getSearchQuery() { + return { + searchText: this.get("searchText"), + shallow: true + }; + }, + + getPaginationQuery(pageNo) { + return { + per_page: 25, + page: pageNo + }; + }, + + trimQuery(query) { + // Remove any undefined values + return _.pickBy(query, _.identity); + }, + actions: { loadMoreOrders(pageNo) { const params = this.trimQuery( diff --git a/app/helpers/orders-package-icon.js b/app/helpers/orders-package-icon.js new file mode 100644 index 000000000..14adbaa90 --- /dev/null +++ b/app/helpers/orders-package-icon.js @@ -0,0 +1,19 @@ +import Ember from "ember"; + +/** + * Returns the icon based on the ordersPackage's state + * + * Example usage: + * + * {{fa-icon (orders-package-icon record.state)} + * + * @param {String} state the orders package state + * @returns {String} + */ +export default Ember.Helper.helper(function([state]) { + return { + cancelled: "ban", + designated: "shopping-basket", + dispatched: "paper-plane" + }[state]; +}); diff --git a/app/helpers/toggle.js b/app/helpers/toggle.js new file mode 100644 index 000000000..fd546ccf0 --- /dev/null +++ b/app/helpers/toggle.js @@ -0,0 +1,20 @@ +import Ember from "ember"; + +/** + * A toggle action builder + * + * Example: + * + * + * + * @param {Object} self the object on which to toggle the property + * @param {String} propName the name of the property to toggle + * @returns {Function} the action that toggles the property + */ +export default Ember.Helper.helper(function([self, propName]) { + return function() { + Ember.set(self, propName, !Ember.get(self, propName)); + }; +}); diff --git a/app/mixins/search_resource.js b/app/mixins/search_resource.js index 76a52f865..787035141 100644 --- a/app/mixins/search_resource.js +++ b/app/mixins/search_resource.js @@ -29,6 +29,9 @@ export default Ember.Mixin.create({ minSearchTextLength: 2, searchText: "", displayResults: false, + searchProps: { + shallow: true + }, // ----- Observers ----- on() { @@ -112,7 +115,7 @@ export default Ember.Mixin.create({ getSearchQuery() { return { searchText: this.get("searchText"), - shallow: true + ...this.get("searchProps") }; }, diff --git a/app/routes/items/search_order.js b/app/routes/items/search_order.js index e1913992b..2498d9f17 100644 --- a/app/routes/items/search_order.js +++ b/app/routes/items/search_order.js @@ -27,11 +27,14 @@ export default AuthorizeRoute.extend({ var path = "items.index"; - if (previousRoute && routeName.indexOf("detail")) { + if (previousRoute && previousRoute.name.indexOf("detail")) { path = previousRoute.name; } - this.set("partialDesignatePath", Boolean(parseInt(window.localStorage.getItem("partial_qnty"), 10))); + this.set( + "partialDesignatePath", + Boolean(parseInt(window.localStorage.getItem("partial_qnty"), 10)) + ); this.set("itemDesignateBackLinkPath", path); }, @@ -51,20 +54,20 @@ export default AuthorizeRoute.extend({ return Ember.RSVP.hash({ item: item || this.store.findRecord("item", params.item_id), - designations: - recentlyUsedDesignations.get("length") - ? recentlyUsedDesignations - : this.get("store").query("designation", { - shallow: true, - recently_used: true - }) + designations: recentlyUsedDesignations.get("length") + ? recentlyUsedDesignations + : this.get("store").query("designation", { + shallow: true, + recently_used: true + }) }); }, setupController(controller, model) { this._super(controller, model); - let isNotPartialRoute = !this.get("partialDesignatePath") && + let isNotPartialRoute = + !this.get("partialDesignatePath") && !parseInt(window.localStorage.getItem("partial_qnty"), 10); controller.set("notPartialRoute", isNotPartialRoute); diff --git a/app/services/defer-service.js b/app/services/defer-service.js new file mode 100644 index 000000000..09a50faa8 --- /dev/null +++ b/app/services/defer-service.js @@ -0,0 +1,69 @@ +/** + * Experimental : + * Given that a lot of UI selection overlays (e.g search_orders, search_organisation) + * are built as separate pages (arguable imo), there's no easy way to "return" some data + * + * It gets very messy to create async flows that require data from multiple places. + * + * So here's a little experiment on a potential approach: + * + * - We create a deferred promise, which is assigned an ID + * - We transition to a page (e.g search_orders) and we pass it this ID + * - The page does it's thing, and resolves the deferred promise + * - the flow continues on the other side + * + * + * e.g + * // Search orders page + * + * onOrderSelected(order) { + * DeferredService.resolve(this.params.deferId, order); + * } + * + * // Some other page + * + * const order = await DeferredService.deferredTransition( + * 'search_orders' + * ) + * doSomething(order) + * + * + */ + +import Ember from "ember"; + +export default Ember.Service.extend({ + init() { + this.nextId = 1; + this.cache = {}; + }, + + defer() { + const deferred = Ember.RSVP.defer(); + const deferId = this.nextId++; + + this.cache[deferId] = deferred; + + return [deferId, deferred.promise]; + }, + + deferredTransition(route, routeParams) { + const [deferId, promise] = this.defer(); + this.transitionTo(route, { ...routeParams, deferId }); + return promise; + }, + + resolve(deferId, result) { + _.pop(deferId).resolve(result); + }, + + reject(deferId, error) { + _.pop(deferId).reject(error); + }, + + _pop(deferId) { + const deferred = this.cache[deferId]; + delete this.cache[deferId]; + return deferred; + } +}); diff --git a/app/services/designation-service.js b/app/services/designation-service.js new file mode 100644 index 000000000..5aa8a42a4 --- /dev/null +++ b/app/services/designation-service.js @@ -0,0 +1,102 @@ +import Ember from "ember"; +import ApiBaseService from "./api-base-service"; + +function ID(modelOrId) { + if (modelOrId.get) { + return modelOrId.get("id"); + } + return modelOrId; +} + +/** + * Designation Service + * + * @description Control of designation of packages to orders + *
Notes: + *
* Stock currently use the 'designation' model, which in reality is + *
the 'order' model. We're trying to move away from this model name. + *
+ *
* 'Designation' here refers to the assignment of a package to an order + *
which mostly involves operations on the orders_package join table + * + */ +export default ApiBaseService.extend({ + store: Ember.inject.service(), + + /** + * Runs a remote action on the specified orders_package + * + * @param {String|Model} ordersPackage + * @param {Stirng} actionName + * @param {Object} [params={}] + * @returns {Promise} + */ + async execAction(ordersPackage, actionName, params = {}) { + const id = ID(ordersPackage); + const url = `/orders_packages/${id}/actions/${actionName}`; + + const data = await this.PUT(url, params); + + this.get("store").pushPayload(data); + return this.get("store").peekRecord("orders_package", id); + }, + + /** + * Cancels an orders_package + * Shorthand for execAction(..., 'cancel') + * + * @param {String|Model} ordersPackage + * @returns {Promise} + */ + cancel(ordersPackage) { + return this.execAction(ordersPackage, "cancel"); + }, + + /** + * Dispatches an orders_package + * Shorthand for execAction(..., 'dispatch') + * + * @param {String|Model} ordersPackage + * @returns {Promise} + */ + dispatch(ordersPackage) { + return this.execAction(ordersPackage, "dispatch"); + }, + + /** + * Undispatches an orders_package + * Shorthand for execAction(..., 'undispatch') + * + * @param {String|Model} ordersPackage + * @returns {Promise} + */ + undispatch(ordersPackage) { + return this.execAction(ordersPackage, "undispatch"); + }, + + /** + * Redesignates the orders_package to a different order + * Shorthand for execAction(..., 'redesignate') + * + * @param {String|Model} ordersPackage The orders_package to update (or its ID) + * @param {String|Model} newOrder The new order to designate to (or its ID) + * @returns {Promise} + */ + redesignate(ordersPackage, newOrder) { + return this.execAction(ordersPackage, "redesignate", { + order_id: ID(newOrder) + }); + }, + + /** + * Modifies the quantity of packages needed for the order + * Shorthand for execAction(..., 'edit_quantity') + * + * @param {String|Model} ordersPackage The orders_package to update (or its ID) + * @param {Number} quantity The new quantity + * @returns {Promise} + */ + editQuantity(ordersPackage, quantity) { + return this.execAction(ordersPackage, "undispatch", { quantity }); + } +}); diff --git a/app/services/navigation.js b/app/services/navigation.js new file mode 100644 index 000000000..aa6eed0d8 --- /dev/null +++ b/app/services/navigation.js @@ -0,0 +1,27 @@ +import Ember from "ember"; + +const NavigationService = Ember.Service.extend({ + router: Ember.inject.service(), + + transitions: {}, + + createTransition(name, action) { + this.transitions[name] = action; + }, + + runTransition(name, params, scope = this) { + if (!this.transitions[name]) { + throw `Transition ${name} does not exist`; + } + this.transitions[name].call(scope, this.get("router"), params); + } +}); + +// +// Presets +// +NavigationService.createTransition("GO_BACK", function(router, params) { + window.history.back(); +}); + +export default NavigationService; diff --git a/app/styles/templates/components/_action_drawer.scss b/app/styles/templates/components/_action_drawer.scss new file mode 100644 index 000000000..bb07aeb40 --- /dev/null +++ b/app/styles/templates/components/_action_drawer.scss @@ -0,0 +1,25 @@ +.cmp.action-drawer { + height: 6em; + width: calc(90vw - 2rem); + max-width: 37rem; + background-color: black; + + .action-holder { + white-space: nowrap; + display: flex; + justify-content: flex-end; + padding-right: 1rem; + .action { + width: 6em; + height: 6em; + line-height: 2em; + padding-top: 1em; + text-align: center; + + &.disabled { + opacity: 0.4; + pointer-events: none; + } + } + } +} \ No newline at end of file diff --git a/app/styles/templates/components/_popup_overlay.scss b/app/styles/templates/components/_popup_overlay.scss new file mode 100644 index 000000000..74e2e8a49 --- /dev/null +++ b/app/styles/templates/components/_popup_overlay.scss @@ -0,0 +1,15 @@ +.cmp.popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + transition: transform ease 0.5s; + background-color: #002352; + z-index: 1000; + transform: translateY(100%); + + &.open { + transform: translateY(0%); + } +} \ No newline at end of file diff --git a/app/styles/templates/components/_side_drawer.scss b/app/styles/templates/components/_side_drawer.scss new file mode 100644 index 000000000..e33488fdc --- /dev/null +++ b/app/styles/templates/components/_side_drawer.scss @@ -0,0 +1,30 @@ +.cmp.side-drawer { + height: 6em; + line-height: 6em; + width: 2em; + position: relative; + + .arrow-icon-holder { + font-size: 2rem; + padding-left: 1.5rem; + } + + .content-wrapper { + position: absolute; + right: 2em; + top: 0; + overflow: hidden; + + .content { + transition: transform 0.1s ease-in; + + &.closed { + transform: translateX(100%); + } + + &.open { + transform: translateX(0%); + } + } + } +} \ No newline at end of file diff --git a/app/styles/templates/components/goodcity/_orders_package_block.scss b/app/styles/templates/components/goodcity/_orders_package_block.scss new file mode 100644 index 000000000..080456d4c --- /dev/null +++ b/app/styles/templates/components/goodcity/_orders_package_block.scss @@ -0,0 +1,45 @@ +.row.gc-orders-package-block { + + width: 90vw; + max-width: 40rem; + margin-bottom: 1rem; + + @media #{$medium-only} { + font-size: 0.8rem; + } + @media #{$large-only} { + font-size: 0.8rem; + } + + .sub-block { + height: 6em; + color: white; + + >div { + margin-bottom: 1em; + } + + .side-drawer { + // padding: 1em; + } + + &:first-child { + border-right: 0.2em solid #002352; + } + + &.left { + padding: 1em; + background-color: #27436C; + text-align: center; + } + + &.main { + @include auto-order-state-color('background-color'); + padding: 1em; + padding-left: 1.5em; + svg { + margin-right: 0.4em; + } + } + } +} \ No newline at end of file diff --git a/app/styles/templates/components/goodcity/_orders_search_overlay.scss b/app/styles/templates/components/goodcity/_orders_search_overlay.scss new file mode 100644 index 000000000..5f13dfa3e --- /dev/null +++ b/app/styles/templates/components/goodcity/_orders_search_overlay.scss @@ -0,0 +1,54 @@ +.cmp.orders-search-overlay { + + .main-container { + margin: 0 auto; + max-width: 768px; + + .search-field { + position: relative; + padding: 0; + background-color: $light-grey; + z-index: 1000; + + .input-holder { + padding-right: 1rem; + input { + padding-left: 0.5rem; + width: 97.5%; + margin: 0.5rem; + border-radius: 0.25rem; + + @media #{$small-only} { + width: 95%; + } + } + } + + .icon-holder { + padding-top: 0.9rem; + padding-left: 0.9rem; + i { + font-size: 1.2rem; + cursor: pointer; + color: $body-font-color; + + @media #{$small-only} { + font-size: 1.2rem; + padding: 0.5rem 0.5rem; + top: 0rem; + } + } + } + + .pinned-right { + position: absolute; + top: 1.4rem; + right: 2rem; + } + } + + .orders_search_result { + padding-top: 0px; + } + } +} \ No newline at end of file diff --git a/app/styles/templates/items/detail/_publishing_tab.scss b/app/styles/templates/items/detail/_publishing_tab.scss index 5eaac0fca..79089ac07 100644 --- a/app/styles/templates/items/detail/_publishing_tab.scss +++ b/app/styles/templates/items/detail/_publishing_tab.scss @@ -1,5 +1,5 @@ .item_details_screen .publishing-tab { - padding: 1rem; + padding-left: 1rem; @mixin spaced-out($mul : 1) { margin-top: 1rem * $mul; diff --git a/app/templates/components/action-drawer.hbs b/app/templates/components/action-drawer.hbs new file mode 100644 index 000000000..846e45068 --- /dev/null +++ b/app/templates/components/action-drawer.hbs @@ -0,0 +1,12 @@ +{{#side-drawer}} +
+
+ {{#each actionList as |act|}} +
+
{{fa-icon act.icon size='lg' }}
+
{{ act.label }}
+
+ {{/each}} +
+
+{{/side-drawer}} \ No newline at end of file diff --git a/app/templates/components/goodcity/orders-package-block.hbs b/app/templates/components/goodcity/orders-package-block.hbs new file mode 100644 index 000000000..1d220b234 --- /dev/null +++ b/app/templates/components/goodcity/orders-package-block.hbs @@ -0,0 +1,29 @@ +
+
+
{{fa-icon (orders-package-icon orderPkg.state) size='lg'}}
+
{{orderPkg.quantity}}
+
+
+
+
+ {{fa-icon orderPkg.designation.transportIcon size='lg'}} + {{ orderPkg.designation.code }} +
+
+
+ {{#if orderPkg.designation.isGoodCityOrder}} + {{ orderPkg.designation.gcOrganisation.nameEn }} + {{else}} + {{ orderPkg.designation.organisation.name }} + {{/if}} +
+
+
+ {{action-drawer actionList=actionList}} + {{goodcity/orders-search-overlay + open=openOrderSearch + onSelect=orderSelected + searchProps=orderSearchProps + }} +
+
\ No newline at end of file diff --git a/app/templates/components/goodcity/orders-search-overlay.hbs b/app/templates/components/goodcity/orders-search-overlay.hbs new file mode 100644 index 000000000..27329c91d --- /dev/null +++ b/app/templates/components/goodcity/orders-search-overlay.hbs @@ -0,0 +1,41 @@ +
+ {{#popup-overlay open=open}} +
+
+
+ +
+ +
+ {{focus-textfield + name="searchText" + id=uuid + placeholder=(t "search_min") + value=searchText }} + + {{#if hasSearchText}} + {{fa-icon 'times-circle' size='lg'}} + {{/if}} +
+
+ +
+ +
+
+ {{/popup-overlay}} +
\ No newline at end of file diff --git a/app/templates/components/popup-overlay.hbs b/app/templates/components/popup-overlay.hbs new file mode 100644 index 000000000..83327ca40 --- /dev/null +++ b/app/templates/components/popup-overlay.hbs @@ -0,0 +1,3 @@ + diff --git a/app/templates/components/side-drawer.hbs b/app/templates/components/side-drawer.hbs new file mode 100644 index 000000000..f640b4b1b --- /dev/null +++ b/app/templates/components/side-drawer.hbs @@ -0,0 +1,14 @@ +
+
+ {{#if isOpen}} + {{fa-icon 'angle-right'}} + {{else}} + {{fa-icon 'angle-left'}} + {{/if}} +
+
+
+ {{ yield }} +
+
+
\ No newline at end of file diff --git a/app/templates/items/detail/_tabs.hbs b/app/templates/items/detail/_tabs.hbs index 4c037af05..e74f1a90b 100644 --- a/app/templates/items/detail/_tabs.hbs +++ b/app/templates/items/detail/_tabs.hbs @@ -43,7 +43,7 @@
{{ model.totalDesignatedQty }}
-
{{fa-icon 'eye' size="lg"}}
+
{{fa-icon (if model.allowWebPublish 'eye' 'eye-slash') size="lg"}}
{{ model.availableQty }}
diff --git a/app/templates/items/detail/tabs/publishing.hbs b/app/templates/items/detail/tabs/publishing.hbs index 57ef193af..1a55b3bab 100644 --- a/app/templates/items/detail/tabs/publishing.hbs +++ b/app/templates/items/detail/tabs/publishing.hbs @@ -53,11 +53,10 @@ {{t 'item.related_orders' }} - {{#each model.ordersPackages as |orderPkg|}} + {{#each sortedOrdersPackages as |orderPkg|}}
{{goodcity/orders-package-block orderPkg=orderPkg }}
{{/each}} - {{goodcity/orders-search-overlay open=true}} From 3a6f012a6ed168744ba26893c6292f61b6b6a523 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Wed, 9 Oct 2019 12:43:14 +0800 Subject: [PATCH 26/54] remove bad files --- .../goodcity/orders-package-block.js | 52 ++++++++++++-- .../goodcity/orders-search-overlay.js | 9 +++ app/controllers/orders/index.js | 70 +++---------------- app/services/defer-service.js | 69 ------------------ app/services/navigation.js | 27 ------- 5 files changed, 67 insertions(+), 160 deletions(-) delete mode 100644 app/services/defer-service.js delete mode 100644 app/services/navigation.js diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js index 4bacbed8a..385d3edf1 100644 --- a/app/components/goodcity/orders-package-block.js +++ b/app/components/goodcity/orders-package-block.js @@ -21,6 +21,13 @@ const ACTIONS_SETTINGS = { } }; +const ACTIVE_ORDER_STATES = [ + "submitted", + "processing", + "awaiting_dispatch", + "dispatching" +]; + const FORCE_DISABLE = ["edit_quantity"]; const CANCELLED = {}; @@ -28,10 +35,21 @@ const CANCELLED = {}; /** * UI Component representing an orders_package * + * ----------------------------------------------------- + * | ico | order code | < | + * | | | < | + * | qty | organisation | < | + * ----------------------------------------------------- + * * It provides a drawer from the right side which is populated * with the actions provided by the backend * * Clicking on an action will run it on the API side + * + * @property {Model} orderPkg the orders_package to display + * + * Usage: + * {{goodcity/orders-package-block orderPkg=orderPkg }} */ export default Ember.Component.extend({ store: Ember.inject.service(), @@ -39,9 +57,7 @@ export default Ember.Component.extend({ i18n: Ember.inject.service(), actionRunner: Ember.inject.service("designationService"), orderSearchProps: { - state: ["submitted", "processing", "awaiting_dispatch", "dispatching"].join( - "," - ) + state: ACTIVE_ORDER_STATES.join(",") }, init() { @@ -54,23 +70,45 @@ export default Ember.Component.extend({ // --- PARAMS RESOLUTION + /** + * Triggers the order selection popup, and resolves the promise + * once an order has been selected. + * + * CANCELLED is returned if the user closes the UI + * + * @returns {Promise} + */ selectOrder() { const deferred = Ember.RSVP.defer(); + this.set("openOrderSearch", true); this.set("orderSelected", order => { deferred.resolve(order ? order.get("id") : CANCELLED); this.set("orderSelected", _.noop); }); - this.set("openOrderSearch", true); - return deferred.promise; }, + /** + * Opens a qty selection UI and resolves the promise with the desired + * quanntity + * + * CANCELLED is returned if the user closes the UI + * + * @todo + * @returns {Promise} + */ async selectQuantity() { throw new Error("Not implemennted"); }, + /** + * Given an actionn name, returns an object with all the required properties + * to successfully + * + * CANCELLED if the user did not provide the + */ async fulfillParams(actionName) { const paramNames = _.get(ACTIONS_SETTINGS, `${actionName}.params`, []); @@ -114,6 +152,10 @@ export default Ember.Component.extend({ this.get("messageBox").alert(message); }, + /** + * Runs the desired action + * + */ async runAction(actionName) { const params = await this.fulfillParams(actionName); const ordersPkg = this.get("orderPkg"); diff --git a/app/components/goodcity/orders-search-overlay.js b/app/components/goodcity/orders-search-overlay.js index b8edea560..26848804f 100644 --- a/app/components/goodcity/orders-search-overlay.js +++ b/app/components/goodcity/orders-search-overlay.js @@ -2,6 +2,15 @@ import Ember from "ember"; import _ from "lodash"; import SearchMixin from "stock/mixins/search_resource"; +/** + * An overlay that pops up from the bottom of the screen, allowing the user + * to search and select an order. + * + * The popup *does not* do anythinng to the order apart from returning it + * + * @property {boolean} open whether the popup is visible or not + * @property {function} onSelect callback triggered when an order is selected + */ export default Ember.Component.extend(SearchMixin, { searchProps: {}, autoLoad: true, diff --git a/app/controllers/orders/index.js b/app/controllers/orders/index.js index c7ddcaaee..bc9b559d5 100644 --- a/app/controllers/orders/index.js +++ b/app/controllers/orders/index.js @@ -1,14 +1,17 @@ import Ember from "ember"; import _ from "lodash"; import { STATE_FILTERS } from "../../services/filter-service"; - -export default Ember.Controller.extend({ - minSearchTextLength: 2, - searchedText: "", - displayResults: false, - - filterService: Ember.inject.service(), - utilityMethods: Ember.inject.service(), +import SearchMixin from "stock/mixins/search_resource"; + +export default Ember.Controller.extend(SearchMixin, { + /** + * @type {Boolean}, expected in SearchMixin + **/ + autoLoad: true, + /** + * @type {Number}, perPage in response + **/ + perPage: 25, afterSearch(designations) { if (designations && designations.get("length") > 0) { @@ -18,38 +21,6 @@ export default Ember.Controller.extend({ } }, - on() { - this.showResults(); // Upon opening the page, we populate with results - this.get("filterService").on("change", this, this.reloadResults); - }, - - off() { - this.get("filterService").off("change", this, this.reloadResults); - }, - - onSearchTextChange: Ember.observer("searchText", function() { - if (this.get("searchText").length > this.get("minSearchTextLength")) { - this.reloadResults(); - } - }), - - reloadResults() { - this.hideResults(); - Ember.run.debounce(this, this.showResults, 500); - }, - - hideResults() { - Ember.run(() => { - this.set("displayResults", false); - }); - }, - - showResults() { - Ember.run(() => { - this.set("displayResults", true); - }); - }, - getFilterQuery() { const filterService = this.get("filterService"); const utils = this.get("utilityMethods"); @@ -71,25 +42,6 @@ export default Ember.Controller.extend({ }; }, - getSearchQuery() { - return { - searchText: this.get("searchText"), - shallow: true - }; - }, - - getPaginationQuery(pageNo) { - return { - per_page: 25, - page: pageNo - }; - }, - - trimQuery(query) { - // Remove any undefined values - return _.pickBy(query, _.identity); - }, - actions: { loadMoreOrders(pageNo) { const params = this.trimQuery( diff --git a/app/services/defer-service.js b/app/services/defer-service.js deleted file mode 100644 index 09a50faa8..000000000 --- a/app/services/defer-service.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Experimental : - * Given that a lot of UI selection overlays (e.g search_orders, search_organisation) - * are built as separate pages (arguable imo), there's no easy way to "return" some data - * - * It gets very messy to create async flows that require data from multiple places. - * - * So here's a little experiment on a potential approach: - * - * - We create a deferred promise, which is assigned an ID - * - We transition to a page (e.g search_orders) and we pass it this ID - * - The page does it's thing, and resolves the deferred promise - * - the flow continues on the other side - * - * - * e.g - * // Search orders page - * - * onOrderSelected(order) { - * DeferredService.resolve(this.params.deferId, order); - * } - * - * // Some other page - * - * const order = await DeferredService.deferredTransition( - * 'search_orders' - * ) - * doSomething(order) - * - * - */ - -import Ember from "ember"; - -export default Ember.Service.extend({ - init() { - this.nextId = 1; - this.cache = {}; - }, - - defer() { - const deferred = Ember.RSVP.defer(); - const deferId = this.nextId++; - - this.cache[deferId] = deferred; - - return [deferId, deferred.promise]; - }, - - deferredTransition(route, routeParams) { - const [deferId, promise] = this.defer(); - this.transitionTo(route, { ...routeParams, deferId }); - return promise; - }, - - resolve(deferId, result) { - _.pop(deferId).resolve(result); - }, - - reject(deferId, error) { - _.pop(deferId).reject(error); - }, - - _pop(deferId) { - const deferred = this.cache[deferId]; - delete this.cache[deferId]; - return deferred; - } -}); diff --git a/app/services/navigation.js b/app/services/navigation.js deleted file mode 100644 index aa6eed0d8..000000000 --- a/app/services/navigation.js +++ /dev/null @@ -1,27 +0,0 @@ -import Ember from "ember"; - -const NavigationService = Ember.Service.extend({ - router: Ember.inject.service(), - - transitions: {}, - - createTransition(name, action) { - this.transitions[name] = action; - }, - - runTransition(name, params, scope = this) { - if (!this.transitions[name]) { - throw `Transition ${name} does not exist`; - } - this.transitions[name].call(scope, this.get("router"), params); - } -}); - -// -// Presets -// -NavigationService.createTransition("GO_BACK", function(router, params) { - window.history.back(); -}); - -export default NavigationService; From 2b6761db98c22e6791e8badb6c7eeb0c868544a0 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Wed, 9 Oct 2019 14:53:55 +0800 Subject: [PATCH 27/54] some cleanup --- .../goodcity/orders-package-block.js | 21 +++++---------- app/components/let-alias.js | 9 +++++++ app/controllers/application.js | 1 + app/services/designation-service.js | 27 +++++++++++++++++++ .../templates/components/_action_drawer.scss | 3 +++ .../templates/components/_side_drawer.scss | 3 +++ app/templates/application.hbs | 9 +++++++ .../goodcity/orders-package-block.hbs | 5 ---- app/templates/components/let-alias.hbs | 1 + app/templates/components/popup-overlay.hbs | 4 +++ 10 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 app/components/let-alias.js create mode 100644 app/templates/components/let-alias.hbs diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js index 385d3edf1..3fddaf050 100644 --- a/app/components/goodcity/orders-package-block.js +++ b/app/components/goodcity/orders-package-block.js @@ -55,10 +55,8 @@ export default Ember.Component.extend({ store: Ember.inject.service(), messageBox: Ember.inject.service(), i18n: Ember.inject.service(), - actionRunner: Ember.inject.service("designationService"), - orderSearchProps: { - state: ACTIVE_ORDER_STATES.join(",") - }, + designationService: Ember.inject.service(), + actionRunner: Ember.computed.alias("designationService"), init() { this._super(...arguments); @@ -71,23 +69,18 @@ export default Ember.Component.extend({ // --- PARAMS RESOLUTION /** - * Triggers the order selection popup, and resolves the promise - * once an order has been selected. + * Prompts the user to select a package * * CANCELLED is returned if the user closes the UI * * @returns {Promise} */ - selectOrder() { - const deferred = Ember.RSVP.defer(); - - this.set("openOrderSearch", true); - this.set("orderSelected", order => { - deferred.resolve(order ? order.get("id") : CANCELLED); - this.set("orderSelected", _.noop); + async selectOrder() { + const order = await this.get("designationService").userPickOrder({ + state: ACTIVE_ORDER_STATES.join(",") }); - return deferred.promise; + return order || CANCELLED; }, /** diff --git a/app/components/let-alias.js b/app/components/let-alias.js new file mode 100644 index 000000000..866905dae --- /dev/null +++ b/app/components/let-alias.js @@ -0,0 +1,9 @@ +import Ember from "ember"; + +const AliasComponent = Ember.Component.extend(); + +AliasComponent.reopenClass({ + positionalParams: ["value"] +}); + +export default AliasComponent; diff --git a/app/controllers/application.js b/app/controllers/application.js index 7ba4c6593..47e1fc4e8 100644 --- a/app/controllers/application.js +++ b/app/controllers/application.js @@ -5,6 +5,7 @@ export default Ember.Controller.extend({ subscription: Ember.inject.service(), cordova: Ember.inject.service(), store: Ember.inject.service(), + designationService: Ember.inject.service(), app_id: config.APP.ANDROID_APP_ID, ios_app_id: config.APP.APPLE_APP_ID, appTitle: config.APP.TITLE, diff --git a/app/services/designation-service.js b/app/services/designation-service.js index 5aa8a42a4..3312f62d7 100644 --- a/app/services/designation-service.js +++ b/app/services/designation-service.js @@ -1,5 +1,6 @@ import Ember from "ember"; import ApiBaseService from "./api-base-service"; +import _ from "lodash"; function ID(modelOrId) { if (modelOrId.get) { @@ -23,6 +24,11 @@ function ID(modelOrId) { export default ApiBaseService.extend({ store: Ember.inject.service(), + init() { + this._super(...arguments); + this.set("openOrderSearch", false); + }, + /** * Runs a remote action on the specified orders_package * @@ -98,5 +104,26 @@ export default ApiBaseService.extend({ */ editQuantity(ordersPackage, quantity) { return this.execAction(ordersPackage, "undispatch", { quantity }); + }, + + /** + * Triggers the order selection popup, and resolves the promise + * once an order has been selected. + * + * null is returned if the user closes the UI + * + * @returns {Promise} + */ + userPickOrder(filters = {}) { + const deferred = Ember.RSVP.defer(); + + this.set("orderSearchProps", filters); + this.set("openOrderSearch", true); + this.set("onnOrderSelected", order => { + this.set("onnOrderSelected", _.noop); + deferred.resolve(order ? order.get("id") : null); + }); + + return deferred.promise; } }); diff --git a/app/styles/templates/components/_action_drawer.scss b/app/styles/templates/components/_action_drawer.scss index bb07aeb40..12fe5486f 100644 --- a/app/styles/templates/components/_action_drawer.scss +++ b/app/styles/templates/components/_action_drawer.scss @@ -1,3 +1,6 @@ +// +// Custom drawer that slides open a list of actions +// .cmp.action-drawer { height: 6em; width: calc(90vw - 2rem); diff --git a/app/styles/templates/components/_side_drawer.scss b/app/styles/templates/components/_side_drawer.scss index e33488fdc..01447803c 100644 --- a/app/styles/templates/components/_side_drawer.scss +++ b/app/styles/templates/components/_side_drawer.scss @@ -1,3 +1,6 @@ +// +// Generic drawer that slides from the right +// .cmp.side-drawer { height: 6em; line-height: 6em; diff --git a/app/templates/application.hbs b/app/templates/application.hbs index e104fe596..b6b8869be 100644 --- a/app/templates/application.hbs +++ b/app/templates/application.hbs @@ -14,3 +14,12 @@ {{/init-foundation}} {{outlet}}
+ +{{!-- Service-backed components --}} +{{#let-alias designationService as |s|}} + {{goodcity/orders-search-overlay + open=s.openOrderSearch + onSelect=s.onnOrderSelected + searchProps=s.orderSearchProps + }} +{{/let-alias}} diff --git a/app/templates/components/goodcity/orders-package-block.hbs b/app/templates/components/goodcity/orders-package-block.hbs index 1d220b234..82fbc33a0 100644 --- a/app/templates/components/goodcity/orders-package-block.hbs +++ b/app/templates/components/goodcity/orders-package-block.hbs @@ -20,10 +20,5 @@
{{action-drawer actionList=actionList}} - {{goodcity/orders-search-overlay - open=openOrderSearch - onSelect=orderSelected - searchProps=orderSearchProps - }}
\ No newline at end of file diff --git a/app/templates/components/let-alias.hbs b/app/templates/components/let-alias.hbs new file mode 100644 index 000000000..cb997f110 --- /dev/null +++ b/app/templates/components/let-alias.hbs @@ -0,0 +1 @@ +{{yield value}} diff --git a/app/templates/components/popup-overlay.hbs b/app/templates/components/popup-overlay.hbs index 83327ca40..d603e6436 100644 --- a/app/templates/components/popup-overlay.hbs +++ b/app/templates/components/popup-overlay.hbs @@ -1,3 +1,7 @@ +{{!-- + Generic animated popup that pops open from the bottom of the \ + screen based on its 'open' property + --}} From d55512a0c453dc657962f76de3348a75be3f7642 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Wed, 9 Oct 2019 15:25:48 +0800 Subject: [PATCH 28/54] Update ember-cli-code-coverage to latest version for async/await support --- app/components/let-alias.js | 11 + config/coverage.js | 10 +- package.json | 2 +- yarn.lock | 513 ++++++++++++++++++++++++------------ 4 files changed, 370 insertions(+), 166 deletions(-) diff --git a/app/components/let-alias.js b/app/components/let-alias.js index 866905dae..4a71acb81 100644 --- a/app/components/let-alias.js +++ b/app/components/let-alias.js @@ -1,5 +1,16 @@ import Ember from "ember"; +/** + * Small helper component allowing to rename variables within the .hbs file + * + * Usage: + * + * {{#let-alias model.someReallyLongPropertyName as |short| }} + *
This is {{short}}
+ *
I can use {{short}} many times {{short}}
+ *
because it's very {{short}}
=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" @@ -10677,6 +10849,13 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -10733,6 +10912,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -10813,11 +10997,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -11174,6 +11353,14 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -11183,6 +11370,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + read-pkg@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" @@ -11650,6 +11846,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + require-relative@^0.8.7: version "0.8.7" resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" @@ -11707,7 +11908,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7, resolve@1.1.x: +resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= @@ -11766,6 +11967,13 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2. dependencies: glob "^7.1.3" +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -12478,11 +12686,6 @@ source-map@0.4.x, source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= - source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -12500,13 +12703,6 @@ source-map@~0.1.x: dependencies: amdefine ">=0.0.4" -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= - dependencies: - amdefine ">=0.0.4" - sourcemap-codec@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" @@ -12752,11 +12948,6 @@ string.prototype.codepointat@^0.2.0: resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== -string.prototype.startswith@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/string.prototype.startswith/-/string.prototype.startswith-0.2.0.tgz#da68982e353a4e9ac4a43b450a2045d1c445ae7b" - integrity sha1-2miYLjU6TprEpDtFCiBF0cRFrns= - string_decoder@0.10, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -12898,13 +13089,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12912,6 +13096,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -12990,6 +13181,16 @@ temp@0.9.0: dependencies: rimraf "~2.6.2" +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + test-value@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/test-value/-/test-value-1.1.0.tgz#a09136f72ec043d27c893707c2b159bfad7de93f" @@ -13308,13 +13509,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-is@~1.6.16, type-is@~1.6.17: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -13760,7 +13954,7 @@ which-module@^1.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= -which@1, which@^1.1.1, which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0: +which@1, which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -13803,11 +13997,6 @@ wordwrap@0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= -wordwrap@^1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" From 294f3ee96f09016056f4b097faa641680647f974 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Wed, 9 Oct 2019 15:32:04 +0800 Subject: [PATCH 29/54] userPickOrder() to return the model instead of the id --- app/components/goodcity/orders-package-block.js | 6 +++--- app/services/designation-service.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js index 3fddaf050..d2b8f359d 100644 --- a/app/components/goodcity/orders-package-block.js +++ b/app/components/goodcity/orders-package-block.js @@ -69,18 +69,18 @@ export default Ember.Component.extend({ // --- PARAMS RESOLUTION /** - * Prompts the user to select a package + * Prompts the user to select an order, and returns its id * * CANCELLED is returned if the user closes the UI * - * @returns {Promise} + * @returns {Promise} */ async selectOrder() { const order = await this.get("designationService").userPickOrder({ state: ACTIVE_ORDER_STATES.join(",") }); - return order || CANCELLED; + return order ? order.get("id") : CANCELLED; }, /** diff --git a/app/services/designation-service.js b/app/services/designation-service.js index 3312f62d7..b2c0fd4e9 100644 --- a/app/services/designation-service.js +++ b/app/services/designation-service.js @@ -121,7 +121,7 @@ export default ApiBaseService.extend({ this.set("openOrderSearch", true); this.set("onnOrderSelected", order => { this.set("onnOrderSelected", _.noop); - deferred.resolve(order ? order.get("id") : null); + deferred.resolve(order || null); }); return deferred.promise; From 6502930be047ffd3d422db663a99b0f6c9e87c9e Mon Sep 17 00:00:00 2001 From: akshay gaikwad Date: Wed, 9 Oct 2019 20:57:51 +0530 Subject: [PATCH 30/54] add latest goodcity-lib master commit hash to refer to latest changes --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 2ec3db4d9..5eb4464a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4202,7 +4202,7 @@ cordova-lib@7.1.0: "cordova-lib@https://github.com/crossroads/goodcity-lib.git#master": version "0.0.0" - resolved "https://github.com/crossroads/goodcity-lib.git#f619e0c7ba59b11ebfa3d14356d648591b2982e1" + resolved "https://github.com/crossroads/goodcity-lib.git#35d90c91005a30b6c3d0022067bf16f0a9a40026" dependencies: bower "^1.8.8" codeclimate-test-reporter "^0.5.0" From b9d348d17d5e88bfb69f6c5abf60c126bf98c7d9 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Thu, 10 Oct 2019 15:10:30 +0800 Subject: [PATCH 31/54] Fix tests --- .../goodcity/orders-package-block.js | 22 +++-- tests/acceptance/item-details-tabs-test.js | 91 ++++++++++++++++++- 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js index d2b8f359d..0593849ce 100644 --- a/app/components/goodcity/orders-package-block.js +++ b/app/components/goodcity/orders-package-block.js @@ -121,18 +121,22 @@ export default Ember.Component.extend({ // --- EXECUTION showSpinner() { - if (!this.loadingView) { - this.loadingView = Ember.getOwner(this) - .lookup("component:loading") - .append(); - } + Ember.run(() => { + if (!this.loadingView) { + this.loadingView = Ember.getOwner(this) + .lookup("component:loading") + .append(); + } + }); }, hideSpinner() { - if (this.loadingView) { - this.loadingView.destroy(); - this.loadingView = null; - } + Ember.run(() => { + if (this.loadingView) { + this.loadingView.destroy(); + this.loadingView = null; + } + }); }, onError(reason) { diff --git a/tests/acceptance/item-details-tabs-test.js b/tests/acceptance/item-details-tabs-test.js index 368e06118..cfb6d328f 100644 --- a/tests/acceptance/item-details-tabs-test.js +++ b/tests/acceptance/item-details-tabs-test.js @@ -9,12 +9,19 @@ import FactoryGuy from "ember-data-factory-guy"; import { mockFindAll } from "ember-data-factory-guy"; import MockUtils from "../helpers/mock-utils"; -let App, pkg; +let App, pkg, designationService, execAction, actionsRan; module("Acceptance: Item details tabs", { beforeEach: function() { App = startApp({}, 2); + actionsRan = []; + designationService = App.__container__.lookup("service:designationService"); + execAction = designationService.execAction; + designationService.execAction = async (_, name) => { + actionsRan.push(name); + }; + MockUtils.startSession(); MockUtils.mockEmptyPreload(); MockUtils.mockUserProfile(); @@ -39,9 +46,24 @@ module("Acceptance: Item details tabs", { let packagesLocation = FactoryGuy.make("packages_location", { location: location, item: pkg, - packageId: pkg.get("it"), + packageId: pkg.get("id"), quantity: 1 }); + let ordersPackage = FactoryGuy.make("orders_package", { + packageId: pkg.get("id"), + item: pkg, + designationId: designation.get("id"), + orderId: designation.get("id"), + orderId: designation.get("id"), + designation: designation, + allowedActions: [ + { name: "cancel", enabled: true }, + { name: "dispatch", enabled: true } + ] + }); + mockFindAll("orders_package").returns({ + json: { orders_packages: [ordersPackage.toJSON({ includeId: true })] } + }); mockFindAll("designation").returns({ json: { designations: [designation.toJSON({ includeId: true })] } }); @@ -60,7 +82,8 @@ module("Acceptance: Item details tabs", { responseText: { items: [pkg.toJSON({ includeId: true })], locations: [location.toJSON({ includeId: true })], - packages_locations: [packagesLocation.toJSON({ includeId: true })] + packages_locations: [packagesLocation.toJSON({ includeId: true })], + orders_packages: [ordersPackage.toJSON({ includeId: true })] } }); @@ -77,6 +100,7 @@ module("Acceptance: Item details tabs", { }); }, afterEach: function() { + designationService.execAction = execAction; // restore MockUtils.closeSession(); Ember.run(App, "destroy"); } @@ -191,3 +215,64 @@ test("Publishing tab content", function(assert) { }); }); }); + +test("Publishing tab orders_packages blocks", function(assert) { + assert.equal(currentPath(), "items.detail.info"); + + click(".item_details_screen .tab-container .tab.publishing"); + + andThen(() => { + assert.equal( + $(".gc-orders-package-block .content-wrapper .content").hasClass( + "closed" + ), + true + ); + + click(".gc-orders-package-block .arrow-icon-holder"); + + andThen(() => { + assert.equal( + $(".gc-orders-package-block .content-wrapper .content").hasClass( + "closed" + ), + false + ); + + const ordersPackagesBLocks = $(".gc-orders-package-block"); + assert.equal(ordersPackagesBLocks.length, 1); + + const actions = ordersPackagesBLocks.find(".action-drawer .action"); + assert.equal(actions.length, 2); + assert.equal( + actions + .eq(0) + .text() + .trim(), + "Cancel" + ); + assert.equal( + actions + .eq(1) + .text() + .trim(), + "Dispatch" + ); + + Ember.run(() => { + assert.equal(actionsRan.length, 0); + actions.eq(0).click(); + Ember.run.next(() => { + assert.equal(actionsRan.length, 1); + assert.equal(actionsRan[0], "cancel"); + actions.eq(1).click(); + Ember.run.later(() => { + assert.equal(actionsRan.length, 2); + assert.equal(actionsRan[0], "cancel"); + assert.equal(actionsRan[1], "dispatch"); + }); + }); + }); + }); + }); +}); From ed2f0a364d45dd8a27eeee0d6aa8a9203d1068b4 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Thu, 10 Oct 2019 15:23:30 +0800 Subject: [PATCH 32/54] Add order state icon to orders_package block --- app/components/goodcity/orders-package-block.js | 10 +++++----- app/models/designation.js | 6 ++++-- .../components/goodcity/orders-package-block.hbs | 5 ++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/components/goodcity/orders-package-block.js b/app/components/goodcity/orders-package-block.js index 0593849ce..8c42a0ab3 100644 --- a/app/components/goodcity/orders-package-block.js +++ b/app/components/goodcity/orders-package-block.js @@ -35,11 +35,11 @@ const CANCELLED = {}; /** * UI Component representing an orders_package * - * ----------------------------------------------------- - * | ico | order code | < | - * | | | < | - * | qty | organisation | < | - * ----------------------------------------------------- + * -------------------------------------------------------- + * | op_state | order_code order_state | < | + * | | | < | + * | qty | organisation | < | + * -------------------------------------------------------- * * It provides a drawer from the right side which is populated * with the actions provided by the backend diff --git a/app/models/designation.js b/app/models/designation.js index 359ffc193..d061730ed 100644 --- a/app/models/designation.js +++ b/app/models/designation.js @@ -72,7 +72,9 @@ export default Model.extend({ }), isLocalOrder: Ember.computed("detailType", function() { - return ["LocalOrder", "StockitLocalOrder"].indexOf(this.get("detailType")) > -1; + return ( + ["LocalOrder", "StockitLocalOrder"].indexOf(this.get("detailType")) > -1 + ); }), isGoodCityOrder: Ember.computed.equal("detailType", "GoodCity"), @@ -165,7 +167,7 @@ export default Model.extend({ } ), - transportIcon: Ember.computed("orderTransport", function() { + transportIcon: Ember.computed("isAppointment", "isOnlineOrder", function() { if (this.get("isAppointment")) { return "warehouse"; } else if (this.get("isOnlineOrder")) { diff --git a/app/templates/components/goodcity/orders-package-block.hbs b/app/templates/components/goodcity/orders-package-block.hbs index 82fbc33a0..31c6682a1 100644 --- a/app/templates/components/goodcity/orders-package-block.hbs +++ b/app/templates/components/goodcity/orders-package-block.hbs @@ -5,10 +5,13 @@
-
+
{{fa-icon orderPkg.designation.transportIcon size='lg'}} {{ orderPkg.designation.code }}
+
+ {{fa-icon orderPkg.designation.stateIcon size='lg'}} +
{{#if orderPkg.designation.isGoodCityOrder}} From 245d25a3f2750817a5f77c14b16e2b46e8355af8 Mon Sep 17 00:00:00 2001 From: Patrick R Date: Thu, 10 Oct 2019 15:29:13 +0800 Subject: [PATCH 33/54] Hide side drawer when there are no actionns --- app/styles/templates/components/_side_drawer.scss | 7 +++++++ app/templates/components/action-drawer.hbs | 2 +- app/templates/components/side-drawer.hbs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/styles/templates/components/_side_drawer.scss b/app/styles/templates/components/_side_drawer.scss index 01447803c..53904ceae 100644 --- a/app/styles/templates/components/_side_drawer.scss +++ b/app/styles/templates/components/_side_drawer.scss @@ -7,6 +7,13 @@ width: 2em; position: relative; + &.hidden { + .arrow-icon-holder { + pointer-events: none; + visibility: hidden; + } + } + .arrow-icon-holder { font-size: 2rem; padding-left: 1.5rem; diff --git a/app/templates/components/action-drawer.hbs b/app/templates/components/action-drawer.hbs index 846e45068..14faf56e0 100644 --- a/app/templates/components/action-drawer.hbs +++ b/app/templates/components/action-drawer.hbs @@ -1,4 +1,4 @@ -{{#side-drawer}} +{{#side-drawer hidden=(is-equal actionList.length 0)}}
{{#each actionList as |act|}} diff --git a/app/templates/components/side-drawer.hbs b/app/templates/components/side-drawer.hbs index f640b4b1b..2ea1ae9b5 100644 --- a/app/templates/components/side-drawer.hbs +++ b/app/templates/components/side-drawer.hbs @@ -1,4 +1,4 @@ -
+