h-1.flet.3/components/customer_picker.py
2026-02-21 23:49:15 +09:00

162 lines
5.7 KiB
Python

"""
顧客選択モーダルコンポーネント
Flutter風のModalBottomSheetをFletで実装
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import flet as ft
from typing import List, Callable, Optional
from models.invoice_models import Customer
class CustomerPickerModal:
"""顧客選択モーダル"""
def __init__(self, page: ft.Page, customers: List[Customer],
on_customer_selected: Callable[[Customer], None],
on_customer_deleted: Callable[[Customer], None] = None):
self.page = page
self.customers = customers
self.on_customer_selected = on_customer_selected
self.on_customer_deleted = on_customer_deleted
self.is_open = False
def open(self):
"""モーダルを開く"""
self.is_open = True
self.show_modal()
def close(self):
"""モーダルを閉じる"""
self.is_open = False
self.hide_modal()
def show_modal(self):
"""モーダル表示"""
# 検索テキストフィールド
search_field = ft.TextField(
label="顧客検索",
prefix_icon=ft.Icons.SEARCH,
on_change=self.filter_customers,
expand=True,
)
# 顧客リスト
customer_list = ft.Column([], scroll=ft.ScrollMode.AUTO, expand=True)
# モーダルダイアログ
modal_dialog = ft.AlertDialog(
title=ft.Text("顧客選択"),
content=ft.Column([
search_field,
ft.Container(height=10),
ft.Container(
content=customer_list,
height=300,
width=400,
),
ft.Container(height=10),
ft.Button(
content=ft.Row([
ft.Icon(ft.Icons.ADD),
ft.Text("新規顧客を登録"),
], alignment=ft.MainAxisAlignment.CENTER),
on_click=self.add_new_customer,
bgcolor=ft.Colors.BLUE_GREY_800,
),
], tight=True),
actions=[
ft.TextButton("キャンセル", on_click=lambda _: self.close()),
],
actions_alignment=ft.MainAxisAlignment.END,
)
# ダイアログを開く
self.page.dialog = modal_dialog
modal_dialog.open = True
self.page.update()
# 初期データ表示
self.update_customer_list_simple(customer_list, self.customers)
def hide_modal(self):
"""モーダルを非表示"""
self.page.overlay.clear()
self.page.update()
def filter_customers(self, e=None):
"""顧客フィルタリング"""
search_text = e.control.value.lower() if e else ""
filtered_customers = [
customer for customer in self.customers
if search_text in customer.name.lower() or
search_text in customer.formal_name.lower()
]
self.update_customer_list(filtered_customers)
def update_customer_list_simple(self, customer_list, customers: List[Customer]):
"""顧客リスト更新(シンプル版)"""
customer_list.controls.clear()
for customer in customers:
card = self.create_customer_card(customer)
customer_list.controls.append(card)
self.page.update()
def update_customer_list(self, customers: List[Customer]):
"""顧客リスト更新"""
# ダイアログの顧客リストを更新
if hasattr(self.page, 'dialog') and self.page.dialog:
customer_list = self.page.dialog.content.controls[2].content
self.update_customer_list_simple(customer_list, customers)
def create_customer_card(self, customer: Customer) -> ft.Container:
"""顧客カード作成"""
return ft.Container(
content=ft.Card(
content=ft.Container(
content=ft.Column([
ft.Row([
ft.Text(
customer.formal_name,
size=16,
weight=ft.FontWeight.BOLD,
expand=True,
),
ft.IconButton(
ft.Icons.DELETE,
on_click=lambda _, c=customer: self.delete_customer(c),
icon_color=ft.Colors.RED_400,
),
]),
ft.Container(height=5),
ft.Text(customer.address, size=12, color=ft.Colors.GREY_600),
ft.Text(customer.phone, size=12, color=ft.Colors.GREY_600),
]),
padding=ft.padding.all(15),
),
elevation=2,
),
on_click=lambda _, c=customer: self.select_customer(c),
)
def select_customer(self, customer: Customer):
"""顧客選択"""
if self.on_customer_selected:
self.on_customer_selected(customer)
self.close()
def delete_customer(self, customer: Customer):
"""顧客削除"""
if self.on_customer_deleted:
self.on_customer_deleted(customer)
def add_new_customer(self, e=None):
"""新規顧客追加"""
# TODO: 新規顧客登録画面を開く
logging.info("新規顧客登録")