Add on-screen keyboard for feedback dialog
This commit is contained in:
		
							parent
							
								
									0767b81d0b
								
							
						
					
					
						commit
						78e6704639
					
				
					 2 changed files with 183 additions and 119 deletions
				
			
		
							
								
								
									
										153
									
								
								wuttapos/controls/feedback.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								wuttapos/controls/feedback.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | ||||||
|  | # -*- coding: utf-8; -*- | ||||||
|  | ################################################################################ | ||||||
|  | # | ||||||
|  | #  WuttaPOS -- Pythonic Point of Sale System | ||||||
|  | #  Copyright © 2023 Lance Edgar | ||||||
|  | # | ||||||
|  | #  This file is part of WuttaPOS. | ||||||
|  | # | ||||||
|  | #  WuttaPOS is free software: you can redistribute it and/or modify it under the | ||||||
|  | #  terms of the GNU General Public License as published by the Free Software | ||||||
|  | #  Foundation, either version 3 of the License, or (at your option) any later | ||||||
|  | #  version. | ||||||
|  | # | ||||||
|  | #  WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY | ||||||
|  | #  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||||
|  | #  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more | ||||||
|  | #  details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License along with | ||||||
|  | #  WuttaPOS.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | # | ||||||
|  | ################################################################################ | ||||||
|  | """ | ||||||
|  | WuttaPOS - feedback control | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | import flet as ft | ||||||
|  | 
 | ||||||
|  | from .base import WuttaControl | ||||||
|  | from .keyboard import WuttaKeyboard | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class WuttaFeedback(WuttaControl): | ||||||
|  | 
 | ||||||
|  |     default_font_size = 20 | ||||||
|  |     default_button_height = 60 | ||||||
|  | 
 | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         self.on_send = kwargs.pop('on_send', None) | ||||||
|  |         self.on_cancel = kwargs.pop('on_cancel', None) | ||||||
|  |         super().__init__(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |     def build(self): | ||||||
|  | 
 | ||||||
|  |         self.button = ft.Container(content=ft.Text("Feedback", size=self.default_font_size, | ||||||
|  |                                                    weight=ft.FontWeight.BOLD), | ||||||
|  |                                    height=self.default_button_height, | ||||||
|  |                                    width=self.default_button_height * 3, | ||||||
|  |                                    on_click=self.initial_click, | ||||||
|  |                                    alignment=ft.alignment.center, | ||||||
|  |                                    border=ft.border.all(1, 'black'), | ||||||
|  |                                    border_radius=ft.border_radius.all(5), | ||||||
|  |                                    bgcolor='blue') | ||||||
|  | 
 | ||||||
|  |         return self.button | ||||||
|  | 
 | ||||||
|  |     def initial_click(self, e): | ||||||
|  | 
 | ||||||
|  |         self.message = ft.TextField(label="Message", | ||||||
|  |                                     multiline=True, min_lines=5, | ||||||
|  |                                     autofocus=True) | ||||||
|  | 
 | ||||||
|  |         self.dlg = ft.AlertDialog( | ||||||
|  |             modal=True, | ||||||
|  |             title=ft.Text("User Feedback"), | ||||||
|  |             content=ft.Container( | ||||||
|  |                 content=ft.Column( | ||||||
|  |                     [ | ||||||
|  |                         ft.Text("Questions, suggestions, comments, complaints, etc. " | ||||||
|  |                                 "are welcome and may be submitted below. "), | ||||||
|  |                         ft.Divider(), | ||||||
|  |                         self.message, | ||||||
|  |                         ft.Divider(), | ||||||
|  |                         WuttaKeyboard(self.config, on_keypress=self.keypress), | ||||||
|  |                     ], | ||||||
|  |                     expand=True, | ||||||
|  |                 ), | ||||||
|  |                 height=800, | ||||||
|  |             ), | ||||||
|  |             actions=[ | ||||||
|  |                 ft.Row( | ||||||
|  |                     [ | ||||||
|  |                         ft.Container(content=ft.Text("Send Message", | ||||||
|  |                                                      size=self.default_font_size, | ||||||
|  |                                                      color='black', | ||||||
|  |                                                      weight=ft.FontWeight.BOLD), | ||||||
|  |                                      height=self.default_button_height, | ||||||
|  |                                      width=self.default_button_height * 3, | ||||||
|  |                                      alignment=ft.alignment.center, | ||||||
|  |                                      bgcolor='blue', | ||||||
|  |                                      border=ft.border.all(1, 'black'), | ||||||
|  |                                      border_radius=ft.border_radius.all(5), | ||||||
|  |                                      on_click=self.send_feedback), | ||||||
|  |                         ft.Container(content=ft.Text("Cancel", | ||||||
|  |                                                      size=self.default_font_size, | ||||||
|  |                                                      weight=ft.FontWeight.BOLD), | ||||||
|  |                                      height=self.default_button_height, | ||||||
|  |                                      width=self.default_button_height * 2.5, | ||||||
|  |                                      alignment=ft.alignment.center, | ||||||
|  |                                      border=ft.border.all(1, 'black'), | ||||||
|  |                                      border_radius=ft.border_radius.all(5), | ||||||
|  |                                      on_click=self.cancel), | ||||||
|  |                     ], | ||||||
|  |                     alignment=ft.MainAxisAlignment.CENTER, | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         self.page.dialog = self.dlg | ||||||
|  |         self.dlg.open = True | ||||||
|  |         self.page.update() | ||||||
|  | 
 | ||||||
|  |     def keypress(self, key): | ||||||
|  |         if key == '⏎': | ||||||
|  |             self.message.value += '\n' | ||||||
|  |         elif key == '⌫': | ||||||
|  |             self.message.value = self.message.value[:-1] | ||||||
|  |         else: | ||||||
|  |             self.message.value += key | ||||||
|  | 
 | ||||||
|  |         self.message.focus() | ||||||
|  |         self.update() | ||||||
|  | 
 | ||||||
|  |     def cancel(self, e): | ||||||
|  |         self.dlg.open = False | ||||||
|  |         self.page.update() | ||||||
|  | 
 | ||||||
|  |         if self.on_cancel: | ||||||
|  |             self.on_cancel(e) | ||||||
|  | 
 | ||||||
|  |     def send_feedback(self, e): | ||||||
|  |         if self.message.value: | ||||||
|  | 
 | ||||||
|  |             self.app.send_email('pos_feedback', data={ | ||||||
|  |                 'user_name': self.page.session.get('user_display'), | ||||||
|  |                 'message': self.message.value, | ||||||
|  |             }) | ||||||
|  | 
 | ||||||
|  |             self.dlg.open = False | ||||||
|  |             self.page.snack_bar = ft.SnackBar(ft.Text(f"MESSAGE WAS SENT", | ||||||
|  |                                                       color='black', | ||||||
|  |                                                       weight=ft.FontWeight.BOLD), | ||||||
|  |                                               bgcolor='green', | ||||||
|  |                                               duration=1500) | ||||||
|  |             self.page.snack_bar.open = True | ||||||
|  |             self.page.update() | ||||||
|  | 
 | ||||||
|  |             if self.on_send: | ||||||
|  |                 self.on_send() | ||||||
|  | 
 | ||||||
|  |         else: | ||||||
|  |             self.message.focus() | ||||||
|  |             self.page.update() | ||||||
|  | @ -32,6 +32,7 @@ import flet as ft | ||||||
| 
 | 
 | ||||||
| from .base import WuttaView | from .base import WuttaView | ||||||
| from wuttapos.controls.custlookup import WuttaCustomerLookup | from wuttapos.controls.custlookup import WuttaCustomerLookup | ||||||
|  | from wuttapos.controls.feedback import WuttaFeedback | ||||||
| from wuttapos.util import get_pos_batch_handler | from wuttapos.util import get_pos_batch_handler | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -66,6 +67,22 @@ class POSView(WuttaView): | ||||||
|     def get_batch_handler(self): |     def get_batch_handler(self): | ||||||
|         return get_pos_batch_handler(self.config) |         return get_pos_batch_handler(self.config) | ||||||
| 
 | 
 | ||||||
|  |     def reset(self, e=None, update=True): | ||||||
|  |         """ | ||||||
|  |         This is a convenience method, meant only to clear the main | ||||||
|  |         input and set focus to it.  Will also update() the page | ||||||
|  |         by default. | ||||||
|  | 
 | ||||||
|  |         The ``e`` arg is ignored and accepted only so this method may | ||||||
|  |         be registered as an event handler, e.g. ``on_cancel``. | ||||||
|  |         """ | ||||||
|  |         self.set_quantity.data = None | ||||||
|  |         self.set_quantity.value = None | ||||||
|  |         self.main_input.value = '' | ||||||
|  |         self.main_input.focus() | ||||||
|  |         if update: | ||||||
|  |             self.page.update() | ||||||
|  | 
 | ||||||
|     def set_customer(self, customer, batch=None): |     def set_customer(self, customer, batch=None): | ||||||
|         session = self.app.get_session(customer) |         session = self.app.get_session(customer) | ||||||
|         if not batch: |         if not batch: | ||||||
|  | @ -96,13 +113,11 @@ class POSView(WuttaView): | ||||||
|             session.close() |             session.close() | ||||||
| 
 | 
 | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.focus() |             self.reset() | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         def cancel(e): |         def cancel(e): | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.focus() |             self.reset() | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         dlg = ft.AlertDialog( |         dlg = ft.AlertDialog( | ||||||
|             modal=True, |             modal=True, | ||||||
|  | @ -144,9 +159,7 @@ class POSView(WuttaView): | ||||||
| 
 | 
 | ||||||
|         def close(e): |         def close(e): | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.value = '' |             self.reset() | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         font_size = self.default_font_size * 0.8 |         font_size = self.default_font_size * 0.8 | ||||||
|         dlg = ft.AlertDialog( |         dlg = ft.AlertDialog( | ||||||
|  | @ -219,9 +232,7 @@ class POSView(WuttaView): | ||||||
| 
 | 
 | ||||||
|         def cancel(e): |         def cancel(e): | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.value = '' |             self.reset() | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         font_size = self.default_font_size * 0.8 |         font_size = self.default_font_size * 0.8 | ||||||
|         dlg = ft.AlertDialog( |         dlg = ft.AlertDialog( | ||||||
|  | @ -291,21 +302,17 @@ class POSView(WuttaView): | ||||||
|             self.informed_refresh() |             self.informed_refresh() | ||||||
| 
 | 
 | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.value = '' |  | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.snack_bar = ft.SnackBar(ft.Text("CUSTOMER REMOVED", |             self.page.snack_bar = ft.SnackBar(ft.Text("CUSTOMER REMOVED", | ||||||
|                                                       color='black', |                                                       color='black', | ||||||
|                                                       weight=ft.FontWeight.BOLD), |                                                       weight=ft.FontWeight.BOLD), | ||||||
|                                               bgcolor='yellow', |                                               bgcolor='yellow', | ||||||
|                                               duration=1500) |                                               duration=1500) | ||||||
|             self.page.snack_bar.open = True |             self.page.snack_bar.open = True | ||||||
|             self.page.update() |             self.reset() | ||||||
| 
 | 
 | ||||||
|         def cancel(e): |         def cancel(e): | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.value = '' |             self.reset() | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         font_size = self.default_font_size * 0.8 |         font_size = self.default_font_size * 0.8 | ||||||
|         dlg = ft.AlertDialog( |         dlg = ft.AlertDialog( | ||||||
|  | @ -344,9 +351,7 @@ class POSView(WuttaView): | ||||||
|         if customer: |         if customer: | ||||||
| 
 | 
 | ||||||
|             self.set_customer(customer) |             self.set_customer(customer) | ||||||
|             self.main_input.value = '' |             self.reset() | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         else: # customer not found |         else: # customer not found | ||||||
|             self.page.snack_bar = ft.SnackBar(ft.Text(f"CUSTOMER NOT FOUND: {entry}", |             self.page.snack_bar = ft.SnackBar(ft.Text(f"CUSTOMER NOT FOUND: {entry}", | ||||||
|  | @ -355,6 +360,7 @@ class POSView(WuttaView): | ||||||
|                                               bgcolor='yellow', |                                               bgcolor='yellow', | ||||||
|                                               duration=1500) |                                               duration=1500) | ||||||
|             self.page.snack_bar.open = True |             self.page.snack_bar.open = True | ||||||
|  |             # TODO: should use reset() here? | ||||||
|             self.main_input.focus() |             self.main_input.focus() | ||||||
|             self.page.update() |             self.page.update() | ||||||
| 
 | 
 | ||||||
|  | @ -403,91 +409,6 @@ class POSView(WuttaView): | ||||||
|             expand=1, |             expand=1, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         def feedback_click(e): |  | ||||||
| 
 |  | ||||||
|             message = ft.TextField(label="Message", |  | ||||||
|                                    multiline=True, min_lines=5, |  | ||||||
|                                    autofocus=True) |  | ||||||
| 
 |  | ||||||
|             def send_feedback(e): |  | ||||||
| 
 |  | ||||||
|                 if message.value: |  | ||||||
| 
 |  | ||||||
|                     self.app.send_email('pos_feedback', data={ |  | ||||||
|                         'user_name': self.page.session.get('user_display'), |  | ||||||
|                         'message': message.value, |  | ||||||
|                     }) |  | ||||||
| 
 |  | ||||||
|                     dlg.open = False |  | ||||||
| 
 |  | ||||||
|                     self.page.snack_bar = ft.SnackBar(ft.Text(f"MESSAGE WAS SENT", |  | ||||||
|                                                               color='black', |  | ||||||
|                                                               weight=ft.FontWeight.BOLD), |  | ||||||
|                                                       bgcolor='green', |  | ||||||
|                                                       duration=1500) |  | ||||||
|                     self.page.snack_bar.open = True |  | ||||||
| 
 |  | ||||||
|                     self.main_input.focus() |  | ||||||
| 
 |  | ||||||
|                 else: |  | ||||||
|                     message.focus() |  | ||||||
| 
 |  | ||||||
|                 self.page.update() |  | ||||||
| 
 |  | ||||||
|             def cancel(e): |  | ||||||
|                 dlg.open = False |  | ||||||
|                 self.main_input.focus() |  | ||||||
|                 self.page.update() |  | ||||||
| 
 |  | ||||||
|             button_height = self.default_button_size * 0.8 |  | ||||||
|             dlg = ft.AlertDialog( |  | ||||||
|                 modal=True, |  | ||||||
|                 title=ft.Text("User Feedback"), |  | ||||||
|                 content=ft.Container( |  | ||||||
|                     content=ft.Column( |  | ||||||
|                         [ |  | ||||||
|                             ft.Text("Questions, suggestions, comments, complaints, etc. " |  | ||||||
|                                     "are welcome and may be submitted below. "), |  | ||||||
|                             ft.Divider(), |  | ||||||
|                             message, |  | ||||||
|                         ], |  | ||||||
|                         expand=True, |  | ||||||
|                     ), |  | ||||||
|                     height=500, |  | ||||||
|                 ), |  | ||||||
|                 actions=[ |  | ||||||
|                     ft.Row( |  | ||||||
|                         [ |  | ||||||
|                             ft.Container(content=ft.Text("Send Message", |  | ||||||
|                                                          size=self.default_font_size, |  | ||||||
|                                                          color='black', |  | ||||||
|                                                          weight=ft.FontWeight.BOLD), |  | ||||||
|                                          height=button_height, |  | ||||||
|                                          width=self.default_button_size * 3, |  | ||||||
|                                          alignment=ft.alignment.center, |  | ||||||
|                                          bgcolor='blue', |  | ||||||
|                                          border=ft.border.all(1, 'black'), |  | ||||||
|                                          border_radius=ft.border_radius.all(5), |  | ||||||
|                                          on_click=send_feedback), |  | ||||||
|                             ft.Container(content=ft.Text("Cancel", |  | ||||||
|                                                          size=self.default_font_size, |  | ||||||
|                                                          weight=ft.FontWeight.BOLD), |  | ||||||
|                                          height=button_height, |  | ||||||
|                                          width=self.default_button_size * 2.5, |  | ||||||
|                                          alignment=ft.alignment.center, |  | ||||||
|                                          border=ft.border.all(1, 'black'), |  | ||||||
|                                          border_radius=ft.border_radius.all(5), |  | ||||||
|                                          on_click=cancel), |  | ||||||
|                         ], |  | ||||||
|                         alignment=ft.MainAxisAlignment.CENTER, |  | ||||||
|                     ), |  | ||||||
|                 ], |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|             self.page.dialog = dlg |  | ||||||
|             dlg.open = True |  | ||||||
|             self.page.update() |  | ||||||
| 
 |  | ||||||
|         def tenkey_click(e): |         def tenkey_click(e): | ||||||
|             value = e.control.content.value |             value = e.control.content.value | ||||||
| 
 | 
 | ||||||
|  | @ -720,8 +641,7 @@ class POSView(WuttaView): | ||||||
| 
 | 
 | ||||||
|             ft.Row( |             ft.Row( | ||||||
|                 [ |                 [ | ||||||
|                     tenkey_button("Feedback", height=70, width=200, |                     WuttaFeedback(self.config, on_send=self.reset, on_cancel=self.reset), | ||||||
|                                   bgcolor='blue', on_click=feedback_click), |  | ||||||
|                     ft.Row( |                     ft.Row( | ||||||
|                         [ |                         [ | ||||||
|                             self.set_quantity, |                             self.set_quantity, | ||||||
|  | @ -848,14 +768,11 @@ class POSView(WuttaView): | ||||||
|             session.close() |             session.close() | ||||||
| 
 | 
 | ||||||
|             self.clear_all() |             self.clear_all() | ||||||
|             self.main_input.value = '' |             self.reset() | ||||||
|             self.main_input.focus() |  | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         def cancel(e): |         def cancel(e): | ||||||
|             dlg.open = False |             dlg.open = False | ||||||
|             self.main_input.focus() |             self.reset() | ||||||
|             self.page.update() |  | ||||||
| 
 | 
 | ||||||
|         session = self.app.make_session() |         session = self.app.make_session() | ||||||
|         batch = self.get_current_batch(session, create=False) |         batch = self.get_current_batch(session, create=False) | ||||||
|  | @ -924,8 +841,7 @@ class POSView(WuttaView): | ||||||
|         session.close() |         session.close() | ||||||
| 
 | 
 | ||||||
|         self.clear_all() |         self.clear_all() | ||||||
|         self.main_input.focus() |         self.reset() | ||||||
|         self.page.update() |  | ||||||
| 
 | 
 | ||||||
|     def clear_all(self): |     def clear_all(self): | ||||||
|         self.items.controls.clear() |         self.items.controls.clear() | ||||||
|  | @ -972,9 +888,4 @@ class POSView(WuttaView): | ||||||
| 
 | 
 | ||||||
|         session.commit() |         session.commit() | ||||||
|         session.close() |         session.close() | ||||||
| 
 |         self.reset() | ||||||
|         self.main_input.value = "" |  | ||||||
|         self.main_input.focus() |  | ||||||
|         self.set_quantity.data = None |  | ||||||
|         self.set_quantity.value = None |  | ||||||
|         self.page.update() |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar