summaryrefslogtreecommitdiff
path: root/addons/point_of_sale/static/src/js/Popups
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/point_of_sale/static/src/js/Popups
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/point_of_sale/static/src/js/Popups')
-rw-r--r--addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js60
-rw-r--r--addons/point_of_sale/static/src/js/Popups/ConfirmPopup.js20
-rw-r--r--addons/point_of_sale/static/src/js/Popups/EditListInput.js19
-rw-r--r--addons/point_of_sale/static/src/js/Popups/EditListPopup.js105
-rw-r--r--addons/point_of_sale/static/src/js/Popups/ErrorBarcodePopup.js26
-rw-r--r--addons/point_of_sale/static/src/js/Popups/ErrorPopup.js24
-rw-r--r--addons/point_of_sale/static/src/js/Popups/ErrorTracebackPopup.js44
-rw-r--r--addons/point_of_sale/static/src/js/Popups/NumberPopup.js79
-rw-r--r--addons/point_of_sale/static/src/js/Popups/OfflineErrorPopup.js29
-rw-r--r--addons/point_of_sale/static/src/js/Popups/OrderImportPopup.js27
-rw-r--r--addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js89
-rw-r--r--addons/point_of_sale/static/src/js/Popups/SelectionPopup.js57
-rw-r--r--addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js39
-rw-r--r--addons/point_of_sale/static/src/js/Popups/TextInputPopup.js34
14 files changed, 652 insertions, 0 deletions
diff --git a/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js
new file mode 100644
index 00000000..6cdd6a04
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/AbstractAwaitablePopup.js
@@ -0,0 +1,60 @@
+odoo.define('point_of_sale.AbstractAwaitablePopup', function (require) {
+ 'use strict';
+
+ const { useExternalListener } = owl.hooks;
+ const PosComponent = require('point_of_sale.PosComponent');
+
+ /**
+ * Implement this abstract class by extending it like so:
+ * ```js
+ * class ConcretePopup extends AbstractAwaitablePopup {
+ * async getPayload() {
+ * return 'result';
+ * }
+ * }
+ * ConcretePopup.template = owl.tags.xml`
+ * <div>
+ * <button t-on-click="confirm">Okay</button>
+ * <button t-on-click="cancel">Cancel</button>
+ * </div>
+ * `
+ * ```
+ *
+ * The concrete popup can now be instantiated and be awaited for
+ * the user's response like so:
+ * ```js
+ * const { confirmed, payload } = await this.showPopup('ConcretePopup');
+ * // based on the implementation above,
+ * // if confirmed, payload = 'result'
+ * // otherwise, payload = null
+ * ```
+ */
+ class AbstractAwaitablePopup extends PosComponent {
+ constructor() {
+ super(...arguments);
+ useExternalListener(window, 'keyup', this._cancelAtEscape);
+ }
+ async confirm() {
+ this.props.resolve({ confirmed: true, payload: await this.getPayload() });
+ this.trigger('close-popup');
+ }
+ cancel() {
+ this.props.resolve({ confirmed: false, payload: null });
+ this.trigger('close-popup');
+ }
+ _cancelAtEscape(event) {
+ if (event.key === 'Escape') {
+ this.cancel();
+ }
+ }
+ /**
+ * Override this in the concrete popup implementation to set the
+ * payload when the popup is confirmed.
+ */
+ async getPayload() {
+ return null;
+ }
+ }
+
+ return AbstractAwaitablePopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/ConfirmPopup.js b/addons/point_of_sale/static/src/js/Popups/ConfirmPopup.js
new file mode 100644
index 00000000..e22c1aaa
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/ConfirmPopup.js
@@ -0,0 +1,20 @@
+odoo.define('point_of_sale.ConfirmPopup', function(require) {
+ 'use strict';
+
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly ConfirmPopupWidget
+ class ConfirmPopup extends AbstractAwaitablePopup {}
+ ConfirmPopup.template = 'ConfirmPopup';
+ ConfirmPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: 'Confirm ?',
+ body: '',
+ };
+
+ Registries.Component.add(ConfirmPopup);
+
+ return ConfirmPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/EditListInput.js b/addons/point_of_sale/static/src/js/Popups/EditListInput.js
new file mode 100644
index 00000000..09b39f21
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/EditListInput.js
@@ -0,0 +1,19 @@
+odoo.define('point_of_sale.EditListInput', function(require) {
+ 'use strict';
+
+ const PosComponent = require('point_of_sale.PosComponent');
+ const Registries = require('point_of_sale.Registries');
+
+ class EditListInput extends PosComponent {
+ onKeyup(event) {
+ if (event.key === "Enter" && event.target.value.trim() !== '') {
+ this.trigger('create-new-item');
+ }
+ }
+ }
+ EditListInput.template = 'EditListInput';
+
+ Registries.Component.add(EditListInput);
+
+ return EditListInput;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/EditListPopup.js b/addons/point_of_sale/static/src/js/Popups/EditListPopup.js
new file mode 100644
index 00000000..ac4b262d
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/EditListPopup.js
@@ -0,0 +1,105 @@
+odoo.define('point_of_sale.EditListPopup', function(require) {
+ 'use strict';
+
+ const { useState } = owl.hooks;
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+ const { useAutoFocusToLast } = require('point_of_sale.custom_hooks');
+
+ /**
+ * Given a array of { id, text }, we show the user this popup to be able to modify this given array.
+ * (used to replace PackLotLinePopupWidget)
+ *
+ * The expected return of showPopup when this popup is used is an array of { _id, [id], text }.
+ * - _id is the assigned unique identifier for each item.
+ * - id is the original id. if not provided, then it means that the item is new.
+ * - text is the modified/unmodified text.
+ *
+ * Example:
+ *
+ * ```
+ * -- perhaps inside a click handler --
+ * // gather the items to edit
+ * const names = [{ id: 1, text: 'Joseph'}, { id: 2, text: 'Kaykay' }];
+ *
+ * // supply the items to the popup and wait for user's response
+ * // when user pressed `confirm` in the popup, the changes he made will be returned by the showPopup function.
+ * const { confirmed, payload: newNames } = await this.showPopup('EditListPopup', {
+ * title: "Can you confirm this item?",
+ * array: names })
+ *
+ * // we then consume the new data. In this example, it is only logged.
+ * if (confirmed) {
+ * console.log(newNames);
+ * // the above might log the following:
+ * // [{ _id: 1, id: 1, text: 'Joseph Caburnay' }, { _id: 2, id: 2, 'Kaykay' }, { _id: 3, 'James' }]
+ * // The result showed that the original item with id=1 was changed to have text 'Joseph Caburnay' from 'Joseph'
+ * // The one with id=2 did not change. And a new item with text='James' is added.
+ * }
+ * ```
+ */
+ class EditListPopup extends AbstractAwaitablePopup {
+ /**
+ * @param {String} title required title of popup
+ * @param {Array} [props.array=[]] the array of { id, text } to be edited or an array of strings
+ * @param {Boolean} [props.isSingleItem=false] true if only allowed to edit single item (the first item)
+ */
+ constructor() {
+ super(...arguments);
+ this._id = 0;
+ this.state = useState({ array: this._initialize(this.props.array) });
+ useAutoFocusToLast();
+ }
+ _nextId() {
+ return this._id++;
+ }
+ _emptyItem() {
+ return {
+ text: '',
+ _id: this._nextId(),
+ };
+ }
+ _initialize(array) {
+ // If no array is provided, we initialize with one empty item.
+ if (array.length === 0) return [this._emptyItem()];
+ // Put _id for each item. It will serve as unique identifier of each item.
+ return array.map((item) => Object.assign({}, { _id: this._nextId() }, typeof item === 'object'? item: { 'text': item}));
+ }
+ removeItem(event) {
+ const itemToRemove = event.detail;
+ this.state.array.splice(
+ this.state.array.findIndex(item => item._id == itemToRemove._id),
+ 1
+ );
+ // We keep a minimum of one empty item in the popup.
+ if (this.state.array.length === 0) {
+ this.state.array.push(this._emptyItem());
+ }
+ }
+ createNewItem() {
+ if (this.props.isSingleItem) return;
+ this.state.array.push(this._emptyItem());
+ }
+ /**
+ * @override
+ */
+ getPayload() {
+ return {
+ newArray: this.state.array
+ .filter((item) => item.text.trim() !== '')
+ .map((item) => Object.assign({}, item)),
+ };
+ }
+ }
+ EditListPopup.template = 'EditListPopup';
+ EditListPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ array: [],
+ isSingleItem: false,
+ };
+
+ Registries.Component.add(EditListPopup);
+
+ return EditListPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/ErrorBarcodePopup.js b/addons/point_of_sale/static/src/js/Popups/ErrorBarcodePopup.js
new file mode 100644
index 00000000..8cf11c40
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/ErrorBarcodePopup.js
@@ -0,0 +1,26 @@
+odoo.define('point_of_sale.ErrorBarcodePopup', function(require) {
+ 'use strict';
+
+ const ErrorPopup = require('point_of_sale.ErrorPopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly ErrorBarcodePopupWidget
+ class ErrorBarcodePopup extends ErrorPopup {
+ get translatedMessage() {
+ return this.env._t(this.props.message);
+ }
+ }
+ ErrorBarcodePopup.template = 'ErrorBarcodePopup';
+ ErrorBarcodePopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: 'Error',
+ body: '',
+ message:
+ 'The Point of Sale could not find any product, client, employee or action associated with the scanned barcode.',
+ };
+
+ Registries.Component.add(ErrorBarcodePopup);
+
+ return ErrorBarcodePopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js b/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js
new file mode 100644
index 00000000..865779c4
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/ErrorPopup.js
@@ -0,0 +1,24 @@
+odoo.define('point_of_sale.ErrorPopup', function(require) {
+ 'use strict';
+
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly ErrorPopupWidget
+ class ErrorPopup extends AbstractAwaitablePopup {
+ mounted() {
+ this.playSound('error');
+ }
+ }
+ ErrorPopup.template = 'ErrorPopup';
+ ErrorPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: 'Error',
+ body: '',
+ };
+
+ Registries.Component.add(ErrorPopup);
+
+ return ErrorPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/ErrorTracebackPopup.js b/addons/point_of_sale/static/src/js/Popups/ErrorTracebackPopup.js
new file mode 100644
index 00000000..1af25e42
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/ErrorTracebackPopup.js
@@ -0,0 +1,44 @@
+odoo.define('point_of_sale.ErrorTracebackPopup', function(require) {
+ 'use strict';
+
+ const ErrorPopup = require('point_of_sale.ErrorPopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly ErrorTracebackPopupWidget
+ class ErrorTracebackPopup extends ErrorPopup {
+ get tracebackUrl() {
+ const blob = new Blob([this.props.body]);
+ const URL = window.URL || window.webkitURL;
+ return URL.createObjectURL(blob);
+ }
+ get tracebackFilename() {
+ return `${this.env._t('error')} ${moment().format('YYYY-MM-DD-HH-mm-ss')}.txt`;
+ }
+ emailTraceback() {
+ const address = this.env.pos.company.email;
+ const subject = this.env._t('IMPORTANT: Bug Report From Odoo Point Of Sale');
+ window.open(
+ 'mailto:' +
+ address +
+ '?subject=' +
+ (subject ? window.encodeURIComponent(subject) : '') +
+ '&body=' +
+ (this.props.body ? window.encodeURIComponent(this.props.body) : '')
+ );
+ }
+ }
+ ErrorTracebackPopup.template = 'ErrorTracebackPopup';
+ ErrorTracebackPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: 'Error with Traceback',
+ body: '',
+ exitButtonIsShown: false,
+ exitButtonText: 'Exit Pos',
+ exitButtonTrigger: 'close-pos'
+ };
+
+ Registries.Component.add(ErrorTracebackPopup);
+
+ return ErrorTracebackPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/NumberPopup.js b/addons/point_of_sale/static/src/js/Popups/NumberPopup.js
new file mode 100644
index 00000000..bf63ba8d
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/NumberPopup.js
@@ -0,0 +1,79 @@
+odoo.define('point_of_sale.NumberPopup', function(require) {
+ 'use strict';
+ var core = require('web.core');
+ var _t = core._t;
+
+ const { useState } = owl;
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const NumberBuffer = require('point_of_sale.NumberBuffer');
+ const { useListener } = require('web.custom_hooks');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly NumberPopupWidget
+ class NumberPopup extends AbstractAwaitablePopup {
+ /**
+ * @param {Object} props
+ * @param {Boolean} props.isPassword Show password popup.
+ * @param {number|null} props.startingValue Starting value of the popup.
+ *
+ * Resolve to { confirmed, payload } when used with showPopup method.
+ * @confirmed {Boolean}
+ * @payload {String}
+ */
+ constructor() {
+ super(...arguments);
+ useListener('accept-input', this.confirm);
+ useListener('close-this-popup', this.cancel);
+ let startingBuffer = '';
+ if (typeof this.props.startingValue === 'number' && this.props.startingValue > 0) {
+ startingBuffer = this.props.startingValue.toString();
+ }
+ this.state = useState({ buffer: startingBuffer });
+ NumberBuffer.use({
+ nonKeyboardInputEvent: 'numpad-click-input',
+ triggerAtEnter: 'accept-input',
+ triggerAtEscape: 'close-this-popup',
+ state: this.state,
+ });
+ }
+ get decimalSeparator() {
+ return this.env._t.database.parameters.decimal_point;
+ }
+ get inputBuffer() {
+ if (this.state.buffer === null) {
+ return '';
+ }
+ if (this.props.isPassword) {
+ return this.state.buffer.replace(/./g, '•');
+ } else {
+ return this.state.buffer;
+ }
+ }
+ confirm(event) {
+ const bufferState = event.detail;
+ if (bufferState.buffer !== '') {
+ super.confirm();
+ }
+ }
+ sendInput(key) {
+ this.trigger('numpad-click-input', { key });
+ }
+ getPayload() {
+ return NumberBuffer.get();
+ }
+ }
+ NumberPopup.template = 'NumberPopup';
+ NumberPopup.defaultProps = {
+ confirmText: _t('Ok'),
+ cancelText: _t('Cancel'),
+ title: _t('Confirm ?'),
+ body: '',
+ cheap: false,
+ startingValue: null,
+ isPassword: false,
+ };
+
+ Registries.Component.add(NumberPopup);
+
+ return NumberPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/OfflineErrorPopup.js b/addons/point_of_sale/static/src/js/Popups/OfflineErrorPopup.js
new file mode 100644
index 00000000..147ed7c4
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/OfflineErrorPopup.js
@@ -0,0 +1,29 @@
+odoo.define('point_of_sale.OfflineErrorPopup', function(require) {
+ 'use strict';
+
+ const ErrorPopup = require('point_of_sale.ErrorPopup');
+ const Registries = require('point_of_sale.Registries');
+
+ /**
+ * This is a special kind of error popup as it introduces
+ * an option to not show it again.
+ */
+ class OfflineErrorPopup extends ErrorPopup {
+ dontShowAgain() {
+ this.constructor.dontShow = true;
+ this.cancel();
+ }
+ }
+ OfflineErrorPopup.template = 'OfflineErrorPopup';
+ OfflineErrorPopup.dontShow = false;
+ OfflineErrorPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: 'Offline Error',
+ body: 'Either the server is inaccessible or browser is not connected online.',
+ };
+
+ Registries.Component.add(OfflineErrorPopup);
+
+ return OfflineErrorPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/OrderImportPopup.js b/addons/point_of_sale/static/src/js/Popups/OrderImportPopup.js
new file mode 100644
index 00000000..c2c35291
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/OrderImportPopup.js
@@ -0,0 +1,27 @@
+odoo.define('point_of_sale.OrderImportPopup', function(require) {
+ 'use strict';
+
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly OrderImportPopupWidget
+ class OrderImportPopup extends AbstractAwaitablePopup {
+ get unpaidSkipped() {
+ return (
+ (this.props.report.unpaid_skipped_existing || 0) +
+ (this.props.report.unpaid_skipped_session || 0)
+ );
+ }
+ getPayload() {}
+ }
+ OrderImportPopup.template = 'OrderImportPopup';
+ OrderImportPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ body: '',
+ };
+
+ Registries.Component.add(OrderImportPopup);
+
+ return OrderImportPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js b/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js
new file mode 100644
index 00000000..b04e55d8
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/ProductConfiguratorPopup.js
@@ -0,0 +1,89 @@
+odoo.define('point_of_sale.ProductConfiguratorPopup', function(require) {
+ 'use strict';
+
+ const { useState, useSubEnv } = owl.hooks;
+ const PosComponent = require('point_of_sale.PosComponent');
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ class ProductConfiguratorPopup extends AbstractAwaitablePopup {
+ constructor() {
+ super(...arguments);
+ useSubEnv({ attribute_components: [] });
+ }
+
+ getPayload() {
+ var selected_attributes = [];
+ var price_extra = 0.0;
+
+ this.env.attribute_components.forEach((attribute_component) => {
+ let { value, extra } = attribute_component.getValue();
+ selected_attributes.push(value);
+ price_extra += extra;
+ });
+
+ return {
+ selected_attributes,
+ price_extra,
+ };
+ }
+ }
+ ProductConfiguratorPopup.template = 'ProductConfiguratorPopup';
+ Registries.Component.add(ProductConfiguratorPopup);
+
+ class BaseProductAttribute extends PosComponent {
+ constructor() {
+ super(...arguments);
+
+ this.env.attribute_components.push(this);
+
+ this.attribute = this.props.attribute;
+ this.values = this.attribute.values;
+ this.state = useState({
+ selected_value: parseFloat(this.values[0].id),
+ custom_value: '',
+ });
+ }
+
+ getValue() {
+ let selected_value = this.values.find((val) => val.id === parseFloat(this.state.selected_value));
+ let value = selected_value.name;
+ if (selected_value.is_custom && this.state.custom_value) {
+ value += `: ${this.state.custom_value}`;
+ }
+
+ return {
+ value,
+ extra: selected_value.price_extra
+ };
+ }
+ }
+
+ class RadioProductAttribute extends BaseProductAttribute {
+ mounted() {
+ // With radio buttons `t-model` selects the default input by searching for inputs with
+ // a matching `value` attribute. In our case, we use `t-att-value` so `value` is
+ // not found yet and no radio is selected by default.
+ // We then manually select the first input of each radio attribute.
+ $(this.el).find('input[type="radio"]:first').prop('checked', true);
+ }
+ }
+ RadioProductAttribute.template = 'RadioProductAttribute';
+ Registries.Component.add(RadioProductAttribute);
+
+ class SelectProductAttribute extends BaseProductAttribute { }
+ SelectProductAttribute.template = 'SelectProductAttribute';
+ Registries.Component.add(SelectProductAttribute);
+
+ class ColorProductAttribute extends BaseProductAttribute {}
+ ColorProductAttribute.template = 'ColorProductAttribute';
+ Registries.Component.add(ColorProductAttribute);
+
+ return {
+ ProductConfiguratorPopup,
+ BaseProductAttribute,
+ RadioProductAttribute,
+ SelectProductAttribute,
+ ColorProductAttribute,
+ };
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js b/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js
new file mode 100644
index 00000000..5321fdea
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/SelectionPopup.js
@@ -0,0 +1,57 @@
+odoo.define('point_of_sale.SelectionPopup', function (require) {
+ 'use strict';
+
+ const { useState } = owl.hooks;
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly SelectionPopupWidget
+ class SelectionPopup extends AbstractAwaitablePopup {
+ /**
+ * Value of the `item` key of the selected element in the Selection
+ * Array is the payload of this popup.
+ *
+ * @param {Object} props
+ * @param {String} [props.confirmText='Confirm']
+ * @param {String} [props.cancelText='Cancel']
+ * @param {String} [props.title='Select']
+ * @param {String} [props.body='']
+ * @param {Array<Selection>} [props.list=[]]
+ * Selection {
+ * id: integer,
+ * label: string,
+ * isSelected: boolean,
+ * item: any,
+ * }
+ */
+ constructor() {
+ super(...arguments);
+ this.state = useState({ selectedId: this.props.list.find((item) => item.isSelected) });
+ }
+ selectItem(itemId) {
+ this.state.selectedId = itemId;
+ this.confirm();
+ }
+ /**
+ * We send as payload of the response the selected item.
+ *
+ * @override
+ */
+ getPayload() {
+ const selected = this.props.list.find((item) => this.state.selectedId === item.id);
+ return selected && selected.item;
+ }
+ }
+ SelectionPopup.template = 'SelectionPopup';
+ SelectionPopup.defaultProps = {
+ confirmText: 'Confirm',
+ cancelText: 'Cancel',
+ title: 'Select',
+ body: '',
+ list: [],
+ };
+
+ Registries.Component.add(SelectionPopup);
+
+ return SelectionPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js b/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js
new file mode 100644
index 00000000..1f2735f6
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/TextAreaPopup.js
@@ -0,0 +1,39 @@
+odoo.define('point_of_sale.TextAreaPopup', function(require) {
+ 'use strict';
+
+ const { useState, useRef } = owl.hooks;
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly TextAreaPopupWidget
+ // IMPROVEMENT: This code is very similar to TextInputPopup.
+ // Combining them would reduce the code.
+ class TextAreaPopup extends AbstractAwaitablePopup {
+ /**
+ * @param {Object} props
+ * @param {string} props.startingValue
+ */
+ constructor() {
+ super(...arguments);
+ this.state = useState({ inputValue: this.props.startingValue });
+ this.inputRef = useRef('input');
+ }
+ mounted() {
+ this.inputRef.el.focus();
+ }
+ getPayload() {
+ return this.state.inputValue;
+ }
+ }
+ TextAreaPopup.template = 'TextAreaPopup';
+ TextAreaPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: '',
+ body: '',
+ };
+
+ Registries.Component.add(TextAreaPopup);
+
+ return TextAreaPopup;
+});
diff --git a/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js
new file mode 100644
index 00000000..4a0612d2
--- /dev/null
+++ b/addons/point_of_sale/static/src/js/Popups/TextInputPopup.js
@@ -0,0 +1,34 @@
+odoo.define('point_of_sale.TextInputPopup', function(require) {
+ 'use strict';
+
+ const { useState, useRef } = owl.hooks;
+ const AbstractAwaitablePopup = require('point_of_sale.AbstractAwaitablePopup');
+ const Registries = require('point_of_sale.Registries');
+
+ // formerly TextInputPopupWidget
+ class TextInputPopup extends AbstractAwaitablePopup {
+ constructor() {
+ super(...arguments);
+ this.state = useState({ inputValue: this.props.startingValue });
+ this.inputRef = useRef('input');
+ }
+ mounted() {
+ this.inputRef.el.focus();
+ }
+ getPayload() {
+ return this.state.inputValue;
+ }
+ }
+ TextInputPopup.template = 'TextInputPopup';
+ TextInputPopup.defaultProps = {
+ confirmText: 'Ok',
+ cancelText: 'Cancel',
+ title: '',
+ body: '',
+ startingValue: '',
+ };
+
+ Registries.Component.add(TextInputPopup);
+
+ return TextInputPopup;
+});