feat: lang
This commit is contained in:
parent
f07d07b3a8
commit
5a83bc4049
@ -49,5 +49,385 @@
|
||||
"profile": "Profile",
|
||||
"@profile": {},
|
||||
"sales_today": "Sales today",
|
||||
"@sales_today": {}
|
||||
"@sales_today": {},
|
||||
"order": "Order",
|
||||
"@order": {},
|
||||
"sales": "Sales",
|
||||
"@sales": {},
|
||||
"finance": "Finance",
|
||||
"@finance": {},
|
||||
"product": "Product",
|
||||
"@product": {},
|
||||
"form": "Form",
|
||||
"@form": {},
|
||||
"schedule": "Schedule",
|
||||
"@schedule": {},
|
||||
"inventory": "Inventory",
|
||||
"@inventory": {},
|
||||
"customer": "Customer",
|
||||
"@customer": {},
|
||||
"purchase": "Purchase",
|
||||
"@purchase": {},
|
||||
"today_summary": "Today's Summary",
|
||||
"@today_summary": {},
|
||||
"today": "Today",
|
||||
"@today": {},
|
||||
"new_customer": "New Customer",
|
||||
"@new_customer": {},
|
||||
"refund": "Refund",
|
||||
"@refund": {},
|
||||
"void_text": "Void",
|
||||
"@void_text": {},
|
||||
"increase": "Increase",
|
||||
"@increase": {},
|
||||
"today_top_product": "Today's Top Product",
|
||||
"@today_top_product": {},
|
||||
"rank": "Rank",
|
||||
"@rank": {},
|
||||
"quantity_sold": "Quantity Sold",
|
||||
"@quantity_sold": {},
|
||||
"total_orders": "Total Orders",
|
||||
"@total_orders": {},
|
||||
"average_price": "Average Price",
|
||||
"@average_price": {},
|
||||
"perfomance": "Performance",
|
||||
"@perfomance": {},
|
||||
"total_sales": "Total Sales",
|
||||
"@total_sales": {},
|
||||
"total_items": "Total Items",
|
||||
"@total_items": {},
|
||||
"summary": "Summary",
|
||||
"@summary": {},
|
||||
"net_sales": "Net Sales",
|
||||
"@net_sales": {},
|
||||
"daily_breakdown": "Daily Breakdown",
|
||||
"@daily_breakdown": {},
|
||||
"orders": "Orders",
|
||||
"@orders": {},
|
||||
"items": "Items",
|
||||
"@items": {},
|
||||
"tax": "Tax",
|
||||
"@tax": {},
|
||||
"discount": "Discount",
|
||||
"@discount": {},
|
||||
"total_purchase": "Total Purchase",
|
||||
"@total_purchase": {},
|
||||
"pending_order": "Pending Order",
|
||||
"@pending_order": {},
|
||||
"history_purchase": "History Purchase",
|
||||
"@history_purchase": {},
|
||||
"all": "All",
|
||||
"@all": {},
|
||||
"select_date_range": "Select Date Range",
|
||||
"@select_date_range": {},
|
||||
"no_date_selected": "No date has been selected yet",
|
||||
"@no_date_selected": {},
|
||||
"selected_date": "Selected Date",
|
||||
"@selected_date": {},
|
||||
"select": "Select",
|
||||
"@select": {},
|
||||
"cancel": "Cancel",
|
||||
"@cancel": {},
|
||||
"total_revenue": "Total Revenue",
|
||||
"@total_revenue": {},
|
||||
"total_expenditures": "Total Expenditures",
|
||||
"@total_expenditures": {},
|
||||
"net_profit": "Net Profit",
|
||||
"@net_profit": {},
|
||||
"margin_profit": "Margin Profit",
|
||||
"@margin_profit": {},
|
||||
"cash_flow_analysis": "Cash Flow Analysis",
|
||||
"@cash_flow_analysis": {},
|
||||
"cash_in": "Cash In",
|
||||
"@cash_in": {},
|
||||
"cash_out": "Cash Out",
|
||||
"@cash_out": {},
|
||||
"net_flow": "Net Flow",
|
||||
"@net_flow": {},
|
||||
"cash_flow_chart": "Cash Flow Chart for {days} Last Days",
|
||||
"@cash_flow_chart": {
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"type": "int",
|
||||
"example": "7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"profit_loss_detail": "Profit & Loss Details",
|
||||
"@profit_loss_detail": {},
|
||||
"gross_sales": "Gross Sales",
|
||||
"@gross_sales": {},
|
||||
"return_text": "Return",
|
||||
"@return_text": {},
|
||||
"cogs": "COGS",
|
||||
"@cogs": {},
|
||||
"cost_of_goods_sold": "Cost of goods sold",
|
||||
"@cost_of_goods_sold": {},
|
||||
"gross_profit": "Gross Profit",
|
||||
"@gross_profit": {},
|
||||
"operating_costs": "Operating Costs",
|
||||
"@operating_costs": {},
|
||||
"sales_category": "Sales Category",
|
||||
"@sales_category": {},
|
||||
"unit": "Unit",
|
||||
"@unit": {},
|
||||
"category_no_data": "There are no data categories yet",
|
||||
"@category_no_data": {},
|
||||
"category_no_data_desc": "Sales category data will appear here",
|
||||
"@category_no_data_desc": {},
|
||||
"product_analytic": "Product Analytic",
|
||||
"@product_analytic": {},
|
||||
"view_all": "View All",
|
||||
"@view_all": {},
|
||||
"sold": "Sold",
|
||||
"@sold": {},
|
||||
"revenue": "Revenue",
|
||||
"@revenue": {},
|
||||
"cost": "Cost",
|
||||
"@cost": {},
|
||||
"profit_per_unit": "Profit per unit",
|
||||
"@profit_per_unit": {},
|
||||
"total_sold": "Total Sold",
|
||||
"@total_sold": {},
|
||||
"ingredients": "Ingredients",
|
||||
"@ingredients": {},
|
||||
"low_stock": "Low Stock",
|
||||
"@low_stock": {},
|
||||
"zero_stock": "Zero Stock",
|
||||
"@zero_stock": {},
|
||||
"stock": "Stock",
|
||||
"@stock": {},
|
||||
"price": "Price",
|
||||
"@price": {},
|
||||
"out_of_stock": "Out of stock",
|
||||
"@out_of_stock": {},
|
||||
"out_of_stock_desc": "Product not available for sale",
|
||||
"@out_of_stock_desc": {},
|
||||
"in_text": "In",
|
||||
"@in_text": {},
|
||||
"out_text": "Out",
|
||||
"@out_text": {},
|
||||
"available": "Available",
|
||||
"@available": {},
|
||||
"total_products": "Total Products",
|
||||
"@total_products": {},
|
||||
"total_ingredients": "Total Ingredients",
|
||||
"@total_ingredients": {},
|
||||
"products": "Products",
|
||||
"@products": {},
|
||||
"value_text": "Value",
|
||||
"@value_text": {},
|
||||
"low_stock_desc": "Immediately reorder at least {stock} pcs",
|
||||
"@low_stock_desc": {
|
||||
"placeholders": {
|
||||
"stock": {
|
||||
"type": "String",
|
||||
"example": "0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"joined": "Joined",
|
||||
"@joined": {},
|
||||
"ago": "ago",
|
||||
"@ago": {},
|
||||
"active": "Active",
|
||||
"@active": {},
|
||||
"inactive": "Inactive",
|
||||
"@inactive": {},
|
||||
"total_amount": "Total Amount",
|
||||
"@total_amount": {},
|
||||
"table": "Table",
|
||||
"@table": {},
|
||||
"remaining": "Remaining",
|
||||
"@remaining": {},
|
||||
"payment": "Payment",
|
||||
"@payment": {},
|
||||
"completed": "Completed",
|
||||
"@completed": {},
|
||||
"pending": "Pending",
|
||||
"@pending": {},
|
||||
"no_order_with_status": "No {status} orders found",
|
||||
"@no_order_with_status": {
|
||||
"placeholders": {
|
||||
"status": {
|
||||
"type": "String",
|
||||
"example": "pending"
|
||||
}
|
||||
}
|
||||
},
|
||||
"order_details": "Order Details",
|
||||
"@order_details": {},
|
||||
"order_number": "Order Number",
|
||||
"@order_number": {},
|
||||
"order_status": "Order Status",
|
||||
"@order_status": {},
|
||||
"order_information": "Order Information",
|
||||
"@order_information": {},
|
||||
"order_type": "Order Type",
|
||||
"@order_type": {},
|
||||
"payment_status": "Payment Status",
|
||||
"@payment_status": {},
|
||||
"created": "Created",
|
||||
"@created": {},
|
||||
"order_item": "Order Item",
|
||||
"@order_item": {},
|
||||
"item": "Item",
|
||||
"@item": {},
|
||||
"each": "Each",
|
||||
"@each": {},
|
||||
"total_item": "Total Item",
|
||||
"@total_item": {},
|
||||
"payment_summary": "Payment Summary",
|
||||
"@payment_summary": {},
|
||||
"subtotal": "Subtotal",
|
||||
"@subtotal": {},
|
||||
"paid": "Paid",
|
||||
"@paid": {},
|
||||
"total": "Total",
|
||||
"@total": {},
|
||||
"payment_method": "Payment Method",
|
||||
"@payment_method": {},
|
||||
"dine_in": "Dine In",
|
||||
"@dine_in": {},
|
||||
"dine_in_experience": "Dine In Experience",
|
||||
"@dine_in_experience": {},
|
||||
"note": "Note",
|
||||
"@note": {},
|
||||
"sales_chart": "Sales Chart",
|
||||
"@sales_chart": {},
|
||||
"no_data_available": "No Data Avaiable",
|
||||
"@no_data_available": {},
|
||||
"total_days_overview": "{days} days overview",
|
||||
"@total_days_overview": {
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"type": "int",
|
||||
"example": "0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sales_data": "Sales Data",
|
||||
"@sales_data": {},
|
||||
"no_sales_data": "No Sales Data",
|
||||
"@no_sales_data": {},
|
||||
"no_sales_data_desc": "Sales data will appear here once transactions are recorded",
|
||||
"@no_sales_data_desc": {},
|
||||
"payment_methods": "Payment Methods",
|
||||
"@payment_methods": {},
|
||||
"payment_methods_desc": "Revenue breakdown by payment method ",
|
||||
"@payment_methods_desc": {},
|
||||
"revenue_share": "Revenue Share",
|
||||
"@revenue_share": {},
|
||||
"no_payment_methods": "No Payment Methods",
|
||||
"@no_payment_methods": {},
|
||||
"no_payment_methods_desc": "Payment method data will appear here once transactions are made",
|
||||
"@no_payment_methods_desc": {},
|
||||
"best_selling_products": "Best Selling Products",
|
||||
"@best_selling_products": {},
|
||||
"highest_sales_ranking": "Highest sales ranking",
|
||||
"@highest_sales_ranking": {},
|
||||
"best_seller": "Best Seller",
|
||||
"@best_seller": {},
|
||||
"top_performer": "Top Performer",
|
||||
"@top_performer": {},
|
||||
"account_information": "Account Information",
|
||||
"@account_information": {},
|
||||
"member_since": "Member Since",
|
||||
"@member_since": {},
|
||||
"edit_profile": "Edit Profile",
|
||||
"@edit_profile": {},
|
||||
"edit_profile_desc": "Update your profile information",
|
||||
"@edit_profile_desc": {},
|
||||
"change_password": "Change Password",
|
||||
"@change_password": {},
|
||||
"change_password_desc": "Update your password",
|
||||
"@change_password_desc": {},
|
||||
"business_settings": "Business Settings",
|
||||
"@business_settings": {},
|
||||
"outlet_information": "Outlet Information",
|
||||
"@outlet_information": {},
|
||||
"outlet_informatio_desc": "Manage your outlet details",
|
||||
"@outlet_informatio_desc": {},
|
||||
"staff_management": "Staff Management",
|
||||
"@staff_management": {},
|
||||
"staff_management_desc": "Manage your staff",
|
||||
"@staff_management_desc": {},
|
||||
"manage_your_products": "Manage Your Products",
|
||||
"@manage_your_products": {},
|
||||
"download_report": "Download Report",
|
||||
"@download_report": {},
|
||||
"download_report_desc": "Download your sales report or inventory report",
|
||||
"@download_report_desc": {},
|
||||
"app_settings": "App Settings",
|
||||
"@app_settings": {},
|
||||
"language_desc": "Select your preferred language",
|
||||
"@language_desc": {},
|
||||
"support": "Support",
|
||||
"@support": {},
|
||||
"help_center": "Help Center",
|
||||
"@help_center": {},
|
||||
"help_center_desc": "Get help from our support team",
|
||||
"@help_center_desc": {},
|
||||
"about": "About",
|
||||
"@about": {},
|
||||
"about_desc": "Learn more about our app",
|
||||
"@about_desc": {},
|
||||
"logout": "Logout",
|
||||
"@logout": {},
|
||||
"logout_desc": "Logout of your account",
|
||||
"@logout_desc": {},
|
||||
"save": "Save",
|
||||
"@save": {},
|
||||
"name": "Name",
|
||||
"@name": {},
|
||||
"name_placeholder": "Please enter your name",
|
||||
"@name_placeholder": {},
|
||||
"password_changed": "Password Changed",
|
||||
"@password_changed": {},
|
||||
"current_password": "Current Password",
|
||||
"@current_password": {},
|
||||
"current_password_placeholder": "Please enter your current password",
|
||||
"@current_password_placeholder": {},
|
||||
"new_password": "New Password",
|
||||
"@new_password": {},
|
||||
"new_password_placeholder": "Please enter your new password",
|
||||
"@new_password_placeholder": {},
|
||||
"new_password_not_same": "New password cannot be same as current password",
|
||||
"@new_password_not_same": {},
|
||||
"general_information": "General Information",
|
||||
"@general_information": {},
|
||||
"address": "Address",
|
||||
"@address": {},
|
||||
"phone_number": "Phone Number",
|
||||
"@phone_number": {},
|
||||
"currency": "Currency",
|
||||
"@currency": {},
|
||||
"tax_rate": "Tax Rate",
|
||||
"@tax_rate": {},
|
||||
"status_text": "Status",
|
||||
"@status_text": {},
|
||||
"coming_soon": "Coming Soon",
|
||||
"@coming_soon": {},
|
||||
"coming_soon_desc": "Something amazing is brewing!\nStay tuned for the big reveal.",
|
||||
"@coming_soon_desc": {},
|
||||
"transaction_report": "Transaction Report",
|
||||
"@transaction_report": {},
|
||||
"transaction_report_desc": "Export all transaction data with detailed analytics",
|
||||
"@transaction_report_desc": {},
|
||||
"invetory_report": "Inventory Report",
|
||||
"@invetory_report": {},
|
||||
"invetory_report_desc": "Export inventory and stock data with trends",
|
||||
"@invetory_report_desc": {},
|
||||
"about_app": "About App",
|
||||
"@about_app": {},
|
||||
"app_information": "App Information",
|
||||
"@app_information": {},
|
||||
"app_name": "App Name",
|
||||
"@app_name": {},
|
||||
"build_number": "Build Number",
|
||||
"@build_number": {},
|
||||
"package_name": "Package Name",
|
||||
"@package_name": {},
|
||||
"device": "Device",
|
||||
"@device": {}
|
||||
}
|
||||
|
||||
@ -49,5 +49,385 @@
|
||||
"profile": "Profil",
|
||||
"@profile": {},
|
||||
"sales_today": "Penjualan hari ini",
|
||||
"@sales_today": {}
|
||||
"@sales_today": {},
|
||||
"order": "Pesanan",
|
||||
"@order": {},
|
||||
"sales": "Penjualan",
|
||||
"@sales": {},
|
||||
"finance": "Keuangan",
|
||||
"@finance": {},
|
||||
"product": "Produk",
|
||||
"@product": {},
|
||||
"form": "Form",
|
||||
"@form": {},
|
||||
"schedule": "Jadwal",
|
||||
"@schedule": {},
|
||||
"inventory": "Inventaris",
|
||||
"@inventory": {},
|
||||
"customer": "Pelanggan",
|
||||
"@customer": {},
|
||||
"purchase": "Pembelian",
|
||||
"@purchase": {},
|
||||
"today_summary": "Ringkasan Hari Ini",
|
||||
"@today_summary": {},
|
||||
"today": "Hari ini",
|
||||
"@today": {},
|
||||
"new_customer": "Pelanggan baru",
|
||||
"@new_customer": {},
|
||||
"refund": "Pengembalian dana",
|
||||
"@refund": {},
|
||||
"void_text": "Dibatalkan",
|
||||
"@void_text": {},
|
||||
"increase": "Bertambah",
|
||||
"@increase": {},
|
||||
"today_top_product": "Produk teratas hari ini",
|
||||
"@today_top_product": {},
|
||||
"rank": "Pangkat",
|
||||
"@rank": {},
|
||||
"quantity_sold": "Kuantiti Terjual",
|
||||
"@quantity_sold": {},
|
||||
"total_orders": "Jumlah Pesanan",
|
||||
"@total_orders": {},
|
||||
"average_price": "Harga Rata-rata",
|
||||
"@average_price": {},
|
||||
"perfomance": "Performa",
|
||||
"@perfomance": {},
|
||||
"total_sales": "Jumlah Penjualan",
|
||||
"@total_sales": {},
|
||||
"total_items": "Jumlah Barang",
|
||||
"@total_items": {},
|
||||
"summary": "Ringkasan",
|
||||
"@summary": {},
|
||||
"net_sales": "Penjualan Bersih",
|
||||
"@net_sales": {},
|
||||
"daily_breakdown": "Perincian Harian",
|
||||
"@daily_breakdown": {},
|
||||
"orders": "Pesanan",
|
||||
"@orders": {},
|
||||
"items": "Barang",
|
||||
"@items": {},
|
||||
"tax": "Pajak",
|
||||
"@tax": {},
|
||||
"discount": "Diskon",
|
||||
"@discount": {},
|
||||
"total_purchase": "Jumlah Pembelian",
|
||||
"@total_purchase": {},
|
||||
"pending_order": "Pesanan Menunggu",
|
||||
"@pending_order": {},
|
||||
"history_purchase": "Riwayat Pembelian",
|
||||
"@history_purchase": {},
|
||||
"all": "Semua",
|
||||
"@all": {},
|
||||
"select_date_range": "Pilih Rentang Tanggal",
|
||||
"@select_date_range": {},
|
||||
"no_date_selected": "Belum ada tanggal dipilih",
|
||||
"@no_date_selected": {},
|
||||
"selected_date": "Tanggal Terpilih",
|
||||
"@selected_date": {},
|
||||
"select": "Pilih",
|
||||
"@select": {},
|
||||
"cancel": "Batal",
|
||||
"@cancel": {},
|
||||
"total_revenue": "Jumlah Pendapatan",
|
||||
"@total_revenue": {},
|
||||
"total_expenditures": "Jumlah Pengeluaran",
|
||||
"@total_expenditures": {},
|
||||
"net_profit": "Keuntungan Bersih",
|
||||
"@net_profit": {},
|
||||
"margin_profit": "Keuntungan Margin",
|
||||
"@margin_profit": {},
|
||||
"cash_flow_analysis": "Analisis Arus Kas",
|
||||
"@cash_flow_analysis": {},
|
||||
"cash_in": "Uang Masuk",
|
||||
"@cash_in": {},
|
||||
"cash_out": "Uang Keluar",
|
||||
"@cash_out": {},
|
||||
"net_flow": "Arus Bersih",
|
||||
"@net_flow": {},
|
||||
"cash_flow_chart": "Grafik Cash Flow ${days} Hari Terakhir",
|
||||
"@cash_flow_chart": {
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"type": "int",
|
||||
"example": "7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"profit_loss_detail": "Detail Untung & Rugi",
|
||||
"@profit_loss_detail": {},
|
||||
"gross_sales": "Penjualan Kotor",
|
||||
"@gross_sales": {},
|
||||
"return_text": "Retur",
|
||||
"@return_text": {},
|
||||
"cogs": "HPP",
|
||||
"@cogs": {},
|
||||
"cost_of_goods_sold": "Harga Pokok Penjualan",
|
||||
"@cost_of_goods_sold": {},
|
||||
"gross_profit": "Keuntungan Kotor",
|
||||
"@gross_profit": {},
|
||||
"operating_costs": "Biaya Operasional",
|
||||
"@operating_costs": {},
|
||||
"sales_category": "Kategori Penjualan",
|
||||
"@sales_category": {},
|
||||
"unit": "Unit",
|
||||
"@unit": {},
|
||||
"category_no_data": "Belum ada data kategori",
|
||||
"@category_no_data": {},
|
||||
"category_no_data_desc": "Data kategori penjualan akan muncul di sini",
|
||||
"@category_no_data_desc": {},
|
||||
"product_analytic": "Analisis Produk",
|
||||
"@product_analytic": {},
|
||||
"view_all": "Lihat Semua",
|
||||
"@view_all": {},
|
||||
"sold": "Terjual",
|
||||
"@sold": {},
|
||||
"revenue": "Pendapatan",
|
||||
"@revenue": {},
|
||||
"cost": "Biaya",
|
||||
"@cost": {},
|
||||
"profit_per_unit": "Keuntungan per unit",
|
||||
"@profit_per_unit": {},
|
||||
"total_sold": "Jumlah Terjual",
|
||||
"@total_sold": {},
|
||||
"ingredients": "Bahan Baku",
|
||||
"@ingredients": {},
|
||||
"low_stock": "Stok Rendah",
|
||||
"@low_stock": {},
|
||||
"zero_stock": "Stok Kosong",
|
||||
"@zero_stock": {},
|
||||
"stock": "Stok",
|
||||
"@stock": {},
|
||||
"price": "Harga",
|
||||
"@price": {},
|
||||
"out_of_stock": "Stok habis",
|
||||
"@out_of_stock": {},
|
||||
"out_of_stock_desc": "Produk tidak tersedia untuk dijual",
|
||||
"@out_of_stock_desc": {},
|
||||
"in_text": "Masuk",
|
||||
"@in_text": {},
|
||||
"out_text": "Keluar",
|
||||
"@out_text": {},
|
||||
"available": "Tersedia",
|
||||
"@available": {},
|
||||
"total_products": "Jumlah Produk",
|
||||
"@total_products": {},
|
||||
"total_ingredients": "Jumlah Bahan Baku",
|
||||
"@total_ingredients": {},
|
||||
"products": "Produk",
|
||||
"@products": {},
|
||||
"value_text": "Nilai",
|
||||
"@value_text": {},
|
||||
"low_stock_desc": "Segera reorder minimal {stock} pcs",
|
||||
"@low_stock_desc": {
|
||||
"placeholders": {
|
||||
"stock": {
|
||||
"type": "String",
|
||||
"example": "0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"joined": "Bergabung",
|
||||
"@joined": {},
|
||||
"ago": "lalu",
|
||||
"@ago": {},
|
||||
"active": "Aktif",
|
||||
"@active": {},
|
||||
"inactive": "Tidak Aktif",
|
||||
"@inactive": {},
|
||||
"total_amount": "Jumlah Total",
|
||||
"@total_amount": {},
|
||||
"table": "Meja",
|
||||
"@table": {},
|
||||
"remaining": "Sisa",
|
||||
"@remaining": {},
|
||||
"payment": "Pembayaran",
|
||||
"@payment": {},
|
||||
"completed": "Selesai",
|
||||
"@completed": {},
|
||||
"pending": "Menunggu",
|
||||
"@pending": {},
|
||||
"no_order_with_status": "Tidak ada pesanan {status} yang ditemukan",
|
||||
"@no_order_with_status": {
|
||||
"placeholders": {
|
||||
"status": {
|
||||
"type": "String",
|
||||
"example": "pending"
|
||||
}
|
||||
}
|
||||
},
|
||||
"order_details": "Detail Pesanan",
|
||||
"@order_details": {},
|
||||
"order_number": "Nomor Pesanan",
|
||||
"@order_number": {},
|
||||
"order_status": "Status Pesanan",
|
||||
"@order_status": {},
|
||||
"order_information": "Informasi Pesanan",
|
||||
"@order_information": {},
|
||||
"order_type": "Tipe Pesanan",
|
||||
"@order_type": {},
|
||||
"payment_status": "Status Pembayaran",
|
||||
"@payment_status": {},
|
||||
"created": "Dibuat",
|
||||
"@created": {},
|
||||
"order_item": "Item Pesanan",
|
||||
"@order_item": {},
|
||||
"item": "Item",
|
||||
"@item": {},
|
||||
"each": "Setiap",
|
||||
"@each": {},
|
||||
"total_item": "Jumlah Item",
|
||||
"@total_item": {},
|
||||
"payment_summary": "Ringkasan Pembayaran",
|
||||
"@payment_summary": {},
|
||||
"subtotal": "Subtotal",
|
||||
"@subtotal": {},
|
||||
"paid": "Dibayar",
|
||||
"@paid": {},
|
||||
"total": "Jumlah",
|
||||
"@total": {},
|
||||
"payment_method": "Metode Pembayaran",
|
||||
"@payment_method": {},
|
||||
"dine_in": "Makan di Tempat",
|
||||
"@dine_in": {},
|
||||
"dine_in_experience": "Pengalaman Bersantap Di Tempat",
|
||||
"@dine_in_experience": {},
|
||||
"note": "Catatan",
|
||||
"@note": {},
|
||||
"sales_chart": "Bagan Penjualan",
|
||||
"@sales_chart": {},
|
||||
"no_data_available": "Tidak Ada Data Tersedia",
|
||||
"@no_data_available": {},
|
||||
"total_days_overview": "ikhtisar {days} hari",
|
||||
"@total_days_overview": {
|
||||
"placeholders": {
|
||||
"days": {
|
||||
"type": "int",
|
||||
"example": "0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sales_data": "Data Penjualan",
|
||||
"@sales_data": {},
|
||||
"no_sales_data": "Tidak ada data penjualan",
|
||||
"@no_sales_data": {},
|
||||
"no_sales_data_desc": "Data penjualan akan muncul di sini setelah transaksi dicatat",
|
||||
"@no_sales_data_desc": {},
|
||||
"payment_methods": "Metode Pembayaran",
|
||||
"@payment_methods": {},
|
||||
"payment_methods_desc": "Rincian pendapatan berdasarkan metode pembayaran ",
|
||||
"@payment_methods_desc": {},
|
||||
"revenue_share": "Bagi Hasil",
|
||||
"@revenue_share": {},
|
||||
"no_payment_methods": "Tidak Ada Metode Pembayaran",
|
||||
"@no_payment_methods": {},
|
||||
"no_payment_methods_desc": "Data metode pembayaran akan muncul di sini setelah transaksi dilakukan",
|
||||
"@no_payment_methods_desc": {},
|
||||
"best_selling_products": "Produk Terlaris",
|
||||
"@best_selling_products": {},
|
||||
"highest_sales_ranking": "Ranking penjualan tertinggi",
|
||||
"@highest_sales_ranking": {},
|
||||
"best_seller": "Penjual Terbaik",
|
||||
"@best_seller": {},
|
||||
"top_performer": "Berkinerja Terbaik",
|
||||
"@top_performer": {},
|
||||
"account_information": "Informasi Akun",
|
||||
"@account_information": {},
|
||||
"member_since": "Member Sejak",
|
||||
"@member_since": {},
|
||||
"edit_profile": "Ubah Profil",
|
||||
"@edit_profile": {},
|
||||
"edit_profile_desc": "Update informasi profil Anda",
|
||||
"@edit_profile_desc": {},
|
||||
"change_password": "Ubah Kata Sandi",
|
||||
"@change_password": {},
|
||||
"change_password_desc": "Update kata sandi Anda",
|
||||
"@change_password_desc": {},
|
||||
"business_settings": "Pengaturan Bisnis",
|
||||
"@business_settings": {},
|
||||
"outlet_information": "Informasi Outlet",
|
||||
"@outlet_information": {},
|
||||
"outlet_informatio_desc": "Kelola informasi outlet Anda",
|
||||
"@outlet_informatio_desc": {},
|
||||
"staff_management": "Manajemen Staff",
|
||||
"@staff_management": {},
|
||||
"staff_management_desc": "Kelola staff Anda",
|
||||
"@staff_management_desc": {},
|
||||
"manage_your_products": "Kelola Produk Anda",
|
||||
"@manage_your_products": {},
|
||||
"download_report": "Unduh Laporan",
|
||||
"@download_report": {},
|
||||
"download_report_desc": "Unduh laporan penjualan atau stok",
|
||||
"@download_report_desc": {},
|
||||
"app_settings": "Pengaturan Aplikasi",
|
||||
"@app_settings": {},
|
||||
"language_desc": "Pilih bahasa aplikasi Anda",
|
||||
"@language_desc": {},
|
||||
"support": "Dukungan",
|
||||
"@support": {},
|
||||
"help_center": "Pusat Bantuan",
|
||||
"@help_center": {},
|
||||
"help_center_desc": "Hubungi tim dukungan kami",
|
||||
"@help_center_desc": {},
|
||||
"about": "Tentang",
|
||||
"@about": {},
|
||||
"about_desc": "Tentang Aplikasi",
|
||||
"@about_desc": {},
|
||||
"logout": "Keluar",
|
||||
"@logout": {},
|
||||
"logout_desc": "Keluar dari akun Anda",
|
||||
"@logout_desc": {},
|
||||
"save": "Simpan",
|
||||
"@save": {},
|
||||
"name": "Nama",
|
||||
"@name": {},
|
||||
"name_placeholder": "Masukkan nama Anda",
|
||||
"@name_placeholder": {},
|
||||
"password_changed": "Kata Sandi Berubah",
|
||||
"@password_changed": {},
|
||||
"current_password": "Kata Sandi Saat Ini",
|
||||
"@current_password": {},
|
||||
"current_password_placeholder": "Masukkan kata sandi saat ini",
|
||||
"@current_password_placeholder": {},
|
||||
"new_password": "Kata Sandi Baru",
|
||||
"@new_password": {},
|
||||
"new_password_placeholder": "Masukkan kata sandi baru",
|
||||
"@new_password_placeholder": {},
|
||||
"new_password_not_same": "Kata Sandi Baru Tidak Sama Dengan Kata Sandi Saat Ini",
|
||||
"@new_password_not_same": {},
|
||||
"general_information": "Informasi Umum",
|
||||
"@general_information": {},
|
||||
"address": "Alamat",
|
||||
"@address": {},
|
||||
"phone_number": "Nomor Telepon",
|
||||
"@phone_number": {},
|
||||
"currency": "Mata Uang",
|
||||
"@currency": {},
|
||||
"tax_rate": "Tarif Pajak",
|
||||
"@tax_rate": {},
|
||||
"status_text": "Status",
|
||||
"@status_text": {},
|
||||
"coming_soon": "Segera Hadir",
|
||||
"@coming_soon": {},
|
||||
"coming_soon_desc": "Sesuatu yang menakjubkan sedang terjadi!\nNantikan pengungkapan besarnya.",
|
||||
"@coming_soon_desc": {},
|
||||
"transaction_report": "Laporan Transaksi",
|
||||
"@transaction_report": {},
|
||||
"transaction_report_desc": "Ekspor semua data transaksi dengan analitik terperinci",
|
||||
"@transaction_report_desc": {},
|
||||
"invetory_report": "Laporan Inventaris",
|
||||
"@invetory_report": {},
|
||||
"invetory_report_desc": "Ekspor inventaris dan data stok dengan tren",
|
||||
"@invetory_report_desc": {},
|
||||
"about_app": "Tentang Aplikasi",
|
||||
"@about_app": {},
|
||||
"app_information": "Informasi Aplikasi",
|
||||
"@app_information": {},
|
||||
"app_name": "Nama Aplikasi",
|
||||
"@app_name": {},
|
||||
"build_number": "Nomor Build",
|
||||
"@build_number": {},
|
||||
"package_name": "Nama Paket",
|
||||
"@package_name": {},
|
||||
"device": "Perangkat",
|
||||
"@device": {}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -82,4 +82,540 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get sales_today => 'Sales today';
|
||||
|
||||
@override
|
||||
String get order => 'Order';
|
||||
|
||||
@override
|
||||
String get sales => 'Sales';
|
||||
|
||||
@override
|
||||
String get finance => 'Finance';
|
||||
|
||||
@override
|
||||
String get product => 'Product';
|
||||
|
||||
@override
|
||||
String get form => 'Form';
|
||||
|
||||
@override
|
||||
String get schedule => 'Schedule';
|
||||
|
||||
@override
|
||||
String get inventory => 'Inventory';
|
||||
|
||||
@override
|
||||
String get customer => 'Customer';
|
||||
|
||||
@override
|
||||
String get purchase => 'Purchase';
|
||||
|
||||
@override
|
||||
String get today_summary => 'Today\'s Summary';
|
||||
|
||||
@override
|
||||
String get today => 'Today';
|
||||
|
||||
@override
|
||||
String get new_customer => 'New Customer';
|
||||
|
||||
@override
|
||||
String get refund => 'Refund';
|
||||
|
||||
@override
|
||||
String get void_text => 'Void';
|
||||
|
||||
@override
|
||||
String get increase => 'Increase';
|
||||
|
||||
@override
|
||||
String get today_top_product => 'Today\'s Top Product';
|
||||
|
||||
@override
|
||||
String get rank => 'Rank';
|
||||
|
||||
@override
|
||||
String get quantity_sold => 'Quantity Sold';
|
||||
|
||||
@override
|
||||
String get total_orders => 'Total Orders';
|
||||
|
||||
@override
|
||||
String get average_price => 'Average Price';
|
||||
|
||||
@override
|
||||
String get perfomance => 'Performance';
|
||||
|
||||
@override
|
||||
String get total_sales => 'Total Sales';
|
||||
|
||||
@override
|
||||
String get total_items => 'Total Items';
|
||||
|
||||
@override
|
||||
String get summary => 'Summary';
|
||||
|
||||
@override
|
||||
String get net_sales => 'Net Sales';
|
||||
|
||||
@override
|
||||
String get daily_breakdown => 'Daily Breakdown';
|
||||
|
||||
@override
|
||||
String get orders => 'Orders';
|
||||
|
||||
@override
|
||||
String get items => 'Items';
|
||||
|
||||
@override
|
||||
String get tax => 'Tax';
|
||||
|
||||
@override
|
||||
String get discount => 'Discount';
|
||||
|
||||
@override
|
||||
String get total_purchase => 'Total Purchase';
|
||||
|
||||
@override
|
||||
String get pending_order => 'Pending Order';
|
||||
|
||||
@override
|
||||
String get history_purchase => 'History Purchase';
|
||||
|
||||
@override
|
||||
String get all => 'All';
|
||||
|
||||
@override
|
||||
String get select_date_range => 'Select Date Range';
|
||||
|
||||
@override
|
||||
String get no_date_selected => 'No date has been selected yet';
|
||||
|
||||
@override
|
||||
String get selected_date => 'Selected Date';
|
||||
|
||||
@override
|
||||
String get select => 'Select';
|
||||
|
||||
@override
|
||||
String get cancel => 'Cancel';
|
||||
|
||||
@override
|
||||
String get total_revenue => 'Total Revenue';
|
||||
|
||||
@override
|
||||
String get total_expenditures => 'Total Expenditures';
|
||||
|
||||
@override
|
||||
String get net_profit => 'Net Profit';
|
||||
|
||||
@override
|
||||
String get margin_profit => 'Margin Profit';
|
||||
|
||||
@override
|
||||
String get cash_flow_analysis => 'Cash Flow Analysis';
|
||||
|
||||
@override
|
||||
String get cash_in => 'Cash In';
|
||||
|
||||
@override
|
||||
String get cash_out => 'Cash Out';
|
||||
|
||||
@override
|
||||
String get net_flow => 'Net Flow';
|
||||
|
||||
@override
|
||||
String cash_flow_chart(int days) {
|
||||
return 'Cash Flow Chart for $days Last Days';
|
||||
}
|
||||
|
||||
@override
|
||||
String get profit_loss_detail => 'Profit & Loss Details';
|
||||
|
||||
@override
|
||||
String get gross_sales => 'Gross Sales';
|
||||
|
||||
@override
|
||||
String get return_text => 'Return';
|
||||
|
||||
@override
|
||||
String get cogs => 'COGS';
|
||||
|
||||
@override
|
||||
String get cost_of_goods_sold => 'Cost of goods sold';
|
||||
|
||||
@override
|
||||
String get gross_profit => 'Gross Profit';
|
||||
|
||||
@override
|
||||
String get operating_costs => 'Operating Costs';
|
||||
|
||||
@override
|
||||
String get sales_category => 'Sales Category';
|
||||
|
||||
@override
|
||||
String get unit => 'Unit';
|
||||
|
||||
@override
|
||||
String get category_no_data => 'There are no data categories yet';
|
||||
|
||||
@override
|
||||
String get category_no_data_desc => 'Sales category data will appear here';
|
||||
|
||||
@override
|
||||
String get product_analytic => 'Product Analytic';
|
||||
|
||||
@override
|
||||
String get view_all => 'View All';
|
||||
|
||||
@override
|
||||
String get sold => 'Sold';
|
||||
|
||||
@override
|
||||
String get revenue => 'Revenue';
|
||||
|
||||
@override
|
||||
String get cost => 'Cost';
|
||||
|
||||
@override
|
||||
String get profit_per_unit => 'Profit per unit';
|
||||
|
||||
@override
|
||||
String get total_sold => 'Total Sold';
|
||||
|
||||
@override
|
||||
String get ingredients => 'Ingredients';
|
||||
|
||||
@override
|
||||
String get low_stock => 'Low Stock';
|
||||
|
||||
@override
|
||||
String get zero_stock => 'Zero Stock';
|
||||
|
||||
@override
|
||||
String get stock => 'Stock';
|
||||
|
||||
@override
|
||||
String get price => 'Price';
|
||||
|
||||
@override
|
||||
String get out_of_stock => 'Out of stock';
|
||||
|
||||
@override
|
||||
String get out_of_stock_desc => 'Product not available for sale';
|
||||
|
||||
@override
|
||||
String get in_text => 'In';
|
||||
|
||||
@override
|
||||
String get out_text => 'Out';
|
||||
|
||||
@override
|
||||
String get available => 'Available';
|
||||
|
||||
@override
|
||||
String get total_products => 'Total Products';
|
||||
|
||||
@override
|
||||
String get total_ingredients => 'Total Ingredients';
|
||||
|
||||
@override
|
||||
String get products => 'Products';
|
||||
|
||||
@override
|
||||
String get value_text => 'Value';
|
||||
|
||||
@override
|
||||
String low_stock_desc(String stock) {
|
||||
return 'Immediately reorder at least $stock pcs';
|
||||
}
|
||||
|
||||
@override
|
||||
String get joined => 'Joined';
|
||||
|
||||
@override
|
||||
String get ago => 'ago';
|
||||
|
||||
@override
|
||||
String get active => 'Active';
|
||||
|
||||
@override
|
||||
String get inactive => 'Inactive';
|
||||
|
||||
@override
|
||||
String get total_amount => 'Total Amount';
|
||||
|
||||
@override
|
||||
String get table => 'Table';
|
||||
|
||||
@override
|
||||
String get remaining => 'Remaining';
|
||||
|
||||
@override
|
||||
String get payment => 'Payment';
|
||||
|
||||
@override
|
||||
String get completed => 'Completed';
|
||||
|
||||
@override
|
||||
String get pending => 'Pending';
|
||||
|
||||
@override
|
||||
String no_order_with_status(String status) {
|
||||
return 'No $status orders found';
|
||||
}
|
||||
|
||||
@override
|
||||
String get order_details => 'Order Details';
|
||||
|
||||
@override
|
||||
String get order_number => 'Order Number';
|
||||
|
||||
@override
|
||||
String get order_status => 'Order Status';
|
||||
|
||||
@override
|
||||
String get order_information => 'Order Information';
|
||||
|
||||
@override
|
||||
String get order_type => 'Order Type';
|
||||
|
||||
@override
|
||||
String get payment_status => 'Payment Status';
|
||||
|
||||
@override
|
||||
String get created => 'Created';
|
||||
|
||||
@override
|
||||
String get order_item => 'Order Item';
|
||||
|
||||
@override
|
||||
String get item => 'Item';
|
||||
|
||||
@override
|
||||
String get each => 'Each';
|
||||
|
||||
@override
|
||||
String get total_item => 'Total Item';
|
||||
|
||||
@override
|
||||
String get payment_summary => 'Payment Summary';
|
||||
|
||||
@override
|
||||
String get subtotal => 'Subtotal';
|
||||
|
||||
@override
|
||||
String get paid => 'Paid';
|
||||
|
||||
@override
|
||||
String get total => 'Total';
|
||||
|
||||
@override
|
||||
String get payment_method => 'Payment Method';
|
||||
|
||||
@override
|
||||
String get dine_in => 'Dine In';
|
||||
|
||||
@override
|
||||
String get dine_in_experience => 'Dine In Experience';
|
||||
|
||||
@override
|
||||
String get note => 'Note';
|
||||
|
||||
@override
|
||||
String get sales_chart => 'Sales Chart';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'No Data Avaiable';
|
||||
|
||||
@override
|
||||
String total_days_overview(int days) {
|
||||
return '$days days overview';
|
||||
}
|
||||
|
||||
@override
|
||||
String get sales_data => 'Sales Data';
|
||||
|
||||
@override
|
||||
String get no_sales_data => 'No Sales Data';
|
||||
|
||||
@override
|
||||
String get no_sales_data_desc => 'Sales data will appear here once transactions are recorded';
|
||||
|
||||
@override
|
||||
String get payment_methods => 'Payment Methods';
|
||||
|
||||
@override
|
||||
String get payment_methods_desc => 'Revenue breakdown by payment method ';
|
||||
|
||||
@override
|
||||
String get revenue_share => 'Revenue Share';
|
||||
|
||||
@override
|
||||
String get no_payment_methods => 'No Payment Methods';
|
||||
|
||||
@override
|
||||
String get no_payment_methods_desc => 'Payment method data will appear here once transactions are made';
|
||||
|
||||
@override
|
||||
String get best_selling_products => 'Best Selling Products';
|
||||
|
||||
@override
|
||||
String get highest_sales_ranking => 'Highest sales ranking';
|
||||
|
||||
@override
|
||||
String get best_seller => 'Best Seller';
|
||||
|
||||
@override
|
||||
String get top_performer => 'Top Performer';
|
||||
|
||||
@override
|
||||
String get account_information => 'Account Information';
|
||||
|
||||
@override
|
||||
String get member_since => 'Member Since';
|
||||
|
||||
@override
|
||||
String get edit_profile => 'Edit Profile';
|
||||
|
||||
@override
|
||||
String get edit_profile_desc => 'Update your profile information';
|
||||
|
||||
@override
|
||||
String get change_password => 'Change Password';
|
||||
|
||||
@override
|
||||
String get change_password_desc => 'Update your password';
|
||||
|
||||
@override
|
||||
String get business_settings => 'Business Settings';
|
||||
|
||||
@override
|
||||
String get outlet_information => 'Outlet Information';
|
||||
|
||||
@override
|
||||
String get outlet_informatio_desc => 'Manage your outlet details';
|
||||
|
||||
@override
|
||||
String get staff_management => 'Staff Management';
|
||||
|
||||
@override
|
||||
String get staff_management_desc => 'Manage your staff';
|
||||
|
||||
@override
|
||||
String get manage_your_products => 'Manage Your Products';
|
||||
|
||||
@override
|
||||
String get download_report => 'Download Report';
|
||||
|
||||
@override
|
||||
String get download_report_desc => 'Download your sales report or inventory report';
|
||||
|
||||
@override
|
||||
String get app_settings => 'App Settings';
|
||||
|
||||
@override
|
||||
String get language_desc => 'Select your preferred language';
|
||||
|
||||
@override
|
||||
String get support => 'Support';
|
||||
|
||||
@override
|
||||
String get help_center => 'Help Center';
|
||||
|
||||
@override
|
||||
String get help_center_desc => 'Get help from our support team';
|
||||
|
||||
@override
|
||||
String get about => 'About';
|
||||
|
||||
@override
|
||||
String get about_desc => 'Learn more about our app';
|
||||
|
||||
@override
|
||||
String get logout => 'Logout';
|
||||
|
||||
@override
|
||||
String get logout_desc => 'Logout of your account';
|
||||
|
||||
@override
|
||||
String get save => 'Save';
|
||||
|
||||
@override
|
||||
String get name => 'Name';
|
||||
|
||||
@override
|
||||
String get name_placeholder => 'Please enter your name';
|
||||
|
||||
@override
|
||||
String get password_changed => 'Password Changed';
|
||||
|
||||
@override
|
||||
String get current_password => 'Current Password';
|
||||
|
||||
@override
|
||||
String get current_password_placeholder => 'Please enter your current password';
|
||||
|
||||
@override
|
||||
String get new_password => 'New Password';
|
||||
|
||||
@override
|
||||
String get new_password_placeholder => 'Please enter your new password';
|
||||
|
||||
@override
|
||||
String get new_password_not_same => 'New password cannot be same as current password';
|
||||
|
||||
@override
|
||||
String get general_information => 'General Information';
|
||||
|
||||
@override
|
||||
String get address => 'Address';
|
||||
|
||||
@override
|
||||
String get phone_number => 'Phone Number';
|
||||
|
||||
@override
|
||||
String get currency => 'Currency';
|
||||
|
||||
@override
|
||||
String get tax_rate => 'Tax Rate';
|
||||
|
||||
@override
|
||||
String get status_text => 'Status';
|
||||
|
||||
@override
|
||||
String get coming_soon => 'Coming Soon';
|
||||
|
||||
@override
|
||||
String get coming_soon_desc => 'Something amazing is brewing!\nStay tuned for the big reveal.';
|
||||
|
||||
@override
|
||||
String get transaction_report => 'Transaction Report';
|
||||
|
||||
@override
|
||||
String get transaction_report_desc => 'Export all transaction data with detailed analytics';
|
||||
|
||||
@override
|
||||
String get invetory_report => 'Inventory Report';
|
||||
|
||||
@override
|
||||
String get invetory_report_desc => 'Export inventory and stock data with trends';
|
||||
|
||||
@override
|
||||
String get about_app => 'About App';
|
||||
|
||||
@override
|
||||
String get app_information => 'App Information';
|
||||
|
||||
@override
|
||||
String get app_name => 'App Name';
|
||||
|
||||
@override
|
||||
String get build_number => 'Build Number';
|
||||
|
||||
@override
|
||||
String get package_name => 'Package Name';
|
||||
|
||||
@override
|
||||
String get device => 'Device';
|
||||
}
|
||||
|
||||
@ -82,4 +82,540 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get sales_today => 'Penjualan hari ini';
|
||||
|
||||
@override
|
||||
String get order => 'Pesanan';
|
||||
|
||||
@override
|
||||
String get sales => 'Penjualan';
|
||||
|
||||
@override
|
||||
String get finance => 'Keuangan';
|
||||
|
||||
@override
|
||||
String get product => 'Produk';
|
||||
|
||||
@override
|
||||
String get form => 'Form';
|
||||
|
||||
@override
|
||||
String get schedule => 'Jadwal';
|
||||
|
||||
@override
|
||||
String get inventory => 'Inventaris';
|
||||
|
||||
@override
|
||||
String get customer => 'Pelanggan';
|
||||
|
||||
@override
|
||||
String get purchase => 'Pembelian';
|
||||
|
||||
@override
|
||||
String get today_summary => 'Ringkasan Hari Ini';
|
||||
|
||||
@override
|
||||
String get today => 'Hari ini';
|
||||
|
||||
@override
|
||||
String get new_customer => 'Pelanggan baru';
|
||||
|
||||
@override
|
||||
String get refund => 'Pengembalian dana';
|
||||
|
||||
@override
|
||||
String get void_text => 'Dibatalkan';
|
||||
|
||||
@override
|
||||
String get increase => 'Bertambah';
|
||||
|
||||
@override
|
||||
String get today_top_product => 'Produk teratas hari ini';
|
||||
|
||||
@override
|
||||
String get rank => 'Pangkat';
|
||||
|
||||
@override
|
||||
String get quantity_sold => 'Kuantiti Terjual';
|
||||
|
||||
@override
|
||||
String get total_orders => 'Jumlah Pesanan';
|
||||
|
||||
@override
|
||||
String get average_price => 'Harga Rata-rata';
|
||||
|
||||
@override
|
||||
String get perfomance => 'Performa';
|
||||
|
||||
@override
|
||||
String get total_sales => 'Jumlah Penjualan';
|
||||
|
||||
@override
|
||||
String get total_items => 'Jumlah Barang';
|
||||
|
||||
@override
|
||||
String get summary => 'Ringkasan';
|
||||
|
||||
@override
|
||||
String get net_sales => 'Penjualan Bersih';
|
||||
|
||||
@override
|
||||
String get daily_breakdown => 'Perincian Harian';
|
||||
|
||||
@override
|
||||
String get orders => 'Pesanan';
|
||||
|
||||
@override
|
||||
String get items => 'Barang';
|
||||
|
||||
@override
|
||||
String get tax => 'Pajak';
|
||||
|
||||
@override
|
||||
String get discount => 'Diskon';
|
||||
|
||||
@override
|
||||
String get total_purchase => 'Jumlah Pembelian';
|
||||
|
||||
@override
|
||||
String get pending_order => 'Pesanan Menunggu';
|
||||
|
||||
@override
|
||||
String get history_purchase => 'Riwayat Pembelian';
|
||||
|
||||
@override
|
||||
String get all => 'Semua';
|
||||
|
||||
@override
|
||||
String get select_date_range => 'Pilih Rentang Tanggal';
|
||||
|
||||
@override
|
||||
String get no_date_selected => 'Belum ada tanggal dipilih';
|
||||
|
||||
@override
|
||||
String get selected_date => 'Tanggal Terpilih';
|
||||
|
||||
@override
|
||||
String get select => 'Pilih';
|
||||
|
||||
@override
|
||||
String get cancel => 'Batal';
|
||||
|
||||
@override
|
||||
String get total_revenue => 'Jumlah Pendapatan';
|
||||
|
||||
@override
|
||||
String get total_expenditures => 'Jumlah Pengeluaran';
|
||||
|
||||
@override
|
||||
String get net_profit => 'Keuntungan Bersih';
|
||||
|
||||
@override
|
||||
String get margin_profit => 'Keuntungan Margin';
|
||||
|
||||
@override
|
||||
String get cash_flow_analysis => 'Analisis Arus Kas';
|
||||
|
||||
@override
|
||||
String get cash_in => 'Uang Masuk';
|
||||
|
||||
@override
|
||||
String get cash_out => 'Uang Keluar';
|
||||
|
||||
@override
|
||||
String get net_flow => 'Arus Bersih';
|
||||
|
||||
@override
|
||||
String cash_flow_chart(int days) {
|
||||
return 'Grafik Cash Flow \$$days Hari Terakhir';
|
||||
}
|
||||
|
||||
@override
|
||||
String get profit_loss_detail => 'Detail Untung & Rugi';
|
||||
|
||||
@override
|
||||
String get gross_sales => 'Penjualan Kotor';
|
||||
|
||||
@override
|
||||
String get return_text => 'Retur';
|
||||
|
||||
@override
|
||||
String get cogs => 'HPP';
|
||||
|
||||
@override
|
||||
String get cost_of_goods_sold => 'Harga Pokok Penjualan';
|
||||
|
||||
@override
|
||||
String get gross_profit => 'Keuntungan Kotor';
|
||||
|
||||
@override
|
||||
String get operating_costs => 'Biaya Operasional';
|
||||
|
||||
@override
|
||||
String get sales_category => 'Kategori Penjualan';
|
||||
|
||||
@override
|
||||
String get unit => 'Unit';
|
||||
|
||||
@override
|
||||
String get category_no_data => 'Belum ada data kategori';
|
||||
|
||||
@override
|
||||
String get category_no_data_desc => 'Data kategori penjualan akan muncul di sini';
|
||||
|
||||
@override
|
||||
String get product_analytic => 'Analisis Produk';
|
||||
|
||||
@override
|
||||
String get view_all => 'Lihat Semua';
|
||||
|
||||
@override
|
||||
String get sold => 'Terjual';
|
||||
|
||||
@override
|
||||
String get revenue => 'Pendapatan';
|
||||
|
||||
@override
|
||||
String get cost => 'Biaya';
|
||||
|
||||
@override
|
||||
String get profit_per_unit => 'Keuntungan per unit';
|
||||
|
||||
@override
|
||||
String get total_sold => 'Jumlah Terjual';
|
||||
|
||||
@override
|
||||
String get ingredients => 'Bahan Baku';
|
||||
|
||||
@override
|
||||
String get low_stock => 'Stok Rendah';
|
||||
|
||||
@override
|
||||
String get zero_stock => 'Stok Kosong';
|
||||
|
||||
@override
|
||||
String get stock => 'Stok';
|
||||
|
||||
@override
|
||||
String get price => 'Harga';
|
||||
|
||||
@override
|
||||
String get out_of_stock => 'Stok habis';
|
||||
|
||||
@override
|
||||
String get out_of_stock_desc => 'Produk tidak tersedia untuk dijual';
|
||||
|
||||
@override
|
||||
String get in_text => 'Masuk';
|
||||
|
||||
@override
|
||||
String get out_text => 'Keluar';
|
||||
|
||||
@override
|
||||
String get available => 'Tersedia';
|
||||
|
||||
@override
|
||||
String get total_products => 'Jumlah Produk';
|
||||
|
||||
@override
|
||||
String get total_ingredients => 'Jumlah Bahan Baku';
|
||||
|
||||
@override
|
||||
String get products => 'Produk';
|
||||
|
||||
@override
|
||||
String get value_text => 'Nilai';
|
||||
|
||||
@override
|
||||
String low_stock_desc(String stock) {
|
||||
return 'Segera reorder minimal $stock pcs';
|
||||
}
|
||||
|
||||
@override
|
||||
String get joined => 'Bergabung';
|
||||
|
||||
@override
|
||||
String get ago => 'lalu';
|
||||
|
||||
@override
|
||||
String get active => 'Aktif';
|
||||
|
||||
@override
|
||||
String get inactive => 'Tidak Aktif';
|
||||
|
||||
@override
|
||||
String get total_amount => 'Jumlah Total';
|
||||
|
||||
@override
|
||||
String get table => 'Meja';
|
||||
|
||||
@override
|
||||
String get remaining => 'Sisa';
|
||||
|
||||
@override
|
||||
String get payment => 'Pembayaran';
|
||||
|
||||
@override
|
||||
String get completed => 'Selesai';
|
||||
|
||||
@override
|
||||
String get pending => 'Menunggu';
|
||||
|
||||
@override
|
||||
String no_order_with_status(String status) {
|
||||
return 'Tidak ada pesanan $status yang ditemukan';
|
||||
}
|
||||
|
||||
@override
|
||||
String get order_details => 'Detail Pesanan';
|
||||
|
||||
@override
|
||||
String get order_number => 'Nomor Pesanan';
|
||||
|
||||
@override
|
||||
String get order_status => 'Status Pesanan';
|
||||
|
||||
@override
|
||||
String get order_information => 'Informasi Pesanan';
|
||||
|
||||
@override
|
||||
String get order_type => 'Tipe Pesanan';
|
||||
|
||||
@override
|
||||
String get payment_status => 'Status Pembayaran';
|
||||
|
||||
@override
|
||||
String get created => 'Dibuat';
|
||||
|
||||
@override
|
||||
String get order_item => 'Item Pesanan';
|
||||
|
||||
@override
|
||||
String get item => 'Item';
|
||||
|
||||
@override
|
||||
String get each => 'Setiap';
|
||||
|
||||
@override
|
||||
String get total_item => 'Jumlah Item';
|
||||
|
||||
@override
|
||||
String get payment_summary => 'Ringkasan Pembayaran';
|
||||
|
||||
@override
|
||||
String get subtotal => 'Subtotal';
|
||||
|
||||
@override
|
||||
String get paid => 'Dibayar';
|
||||
|
||||
@override
|
||||
String get total => 'Jumlah';
|
||||
|
||||
@override
|
||||
String get payment_method => 'Metode Pembayaran';
|
||||
|
||||
@override
|
||||
String get dine_in => 'Makan di Tempat';
|
||||
|
||||
@override
|
||||
String get dine_in_experience => 'Pengalaman Bersantap Di Tempat';
|
||||
|
||||
@override
|
||||
String get note => 'Catatan';
|
||||
|
||||
@override
|
||||
String get sales_chart => 'Bagan Penjualan';
|
||||
|
||||
@override
|
||||
String get no_data_available => 'Tidak Ada Data Tersedia';
|
||||
|
||||
@override
|
||||
String total_days_overview(int days) {
|
||||
return 'ikhtisar $days hari';
|
||||
}
|
||||
|
||||
@override
|
||||
String get sales_data => 'Data Penjualan';
|
||||
|
||||
@override
|
||||
String get no_sales_data => 'Tidak ada data penjualan';
|
||||
|
||||
@override
|
||||
String get no_sales_data_desc => 'Data penjualan akan muncul di sini setelah transaksi dicatat';
|
||||
|
||||
@override
|
||||
String get payment_methods => 'Metode Pembayaran';
|
||||
|
||||
@override
|
||||
String get payment_methods_desc => 'Rincian pendapatan berdasarkan metode pembayaran ';
|
||||
|
||||
@override
|
||||
String get revenue_share => 'Bagi Hasil';
|
||||
|
||||
@override
|
||||
String get no_payment_methods => 'Tidak Ada Metode Pembayaran';
|
||||
|
||||
@override
|
||||
String get no_payment_methods_desc => 'Data metode pembayaran akan muncul di sini setelah transaksi dilakukan';
|
||||
|
||||
@override
|
||||
String get best_selling_products => 'Produk Terlaris';
|
||||
|
||||
@override
|
||||
String get highest_sales_ranking => 'Ranking penjualan tertinggi';
|
||||
|
||||
@override
|
||||
String get best_seller => 'Penjual Terbaik';
|
||||
|
||||
@override
|
||||
String get top_performer => 'Berkinerja Terbaik';
|
||||
|
||||
@override
|
||||
String get account_information => 'Informasi Akun';
|
||||
|
||||
@override
|
||||
String get member_since => 'Member Sejak';
|
||||
|
||||
@override
|
||||
String get edit_profile => 'Ubah Profil';
|
||||
|
||||
@override
|
||||
String get edit_profile_desc => 'Update informasi profil Anda';
|
||||
|
||||
@override
|
||||
String get change_password => 'Ubah Kata Sandi';
|
||||
|
||||
@override
|
||||
String get change_password_desc => 'Update kata sandi Anda';
|
||||
|
||||
@override
|
||||
String get business_settings => 'Pengaturan Bisnis';
|
||||
|
||||
@override
|
||||
String get outlet_information => 'Informasi Outlet';
|
||||
|
||||
@override
|
||||
String get outlet_informatio_desc => 'Kelola informasi outlet Anda';
|
||||
|
||||
@override
|
||||
String get staff_management => 'Manajemen Staff';
|
||||
|
||||
@override
|
||||
String get staff_management_desc => 'Kelola staff Anda';
|
||||
|
||||
@override
|
||||
String get manage_your_products => 'Kelola Produk Anda';
|
||||
|
||||
@override
|
||||
String get download_report => 'Unduh Laporan';
|
||||
|
||||
@override
|
||||
String get download_report_desc => 'Unduh laporan penjualan atau stok';
|
||||
|
||||
@override
|
||||
String get app_settings => 'Pengaturan Aplikasi';
|
||||
|
||||
@override
|
||||
String get language_desc => 'Pilih bahasa aplikasi Anda';
|
||||
|
||||
@override
|
||||
String get support => 'Dukungan';
|
||||
|
||||
@override
|
||||
String get help_center => 'Pusat Bantuan';
|
||||
|
||||
@override
|
||||
String get help_center_desc => 'Hubungi tim dukungan kami';
|
||||
|
||||
@override
|
||||
String get about => 'Tentang';
|
||||
|
||||
@override
|
||||
String get about_desc => 'Tentang Aplikasi';
|
||||
|
||||
@override
|
||||
String get logout => 'Keluar';
|
||||
|
||||
@override
|
||||
String get logout_desc => 'Keluar dari akun Anda';
|
||||
|
||||
@override
|
||||
String get save => 'Simpan';
|
||||
|
||||
@override
|
||||
String get name => 'Nama';
|
||||
|
||||
@override
|
||||
String get name_placeholder => 'Masukkan nama Anda';
|
||||
|
||||
@override
|
||||
String get password_changed => 'Kata Sandi Berubah';
|
||||
|
||||
@override
|
||||
String get current_password => 'Kata Sandi Saat Ini';
|
||||
|
||||
@override
|
||||
String get current_password_placeholder => 'Masukkan kata sandi saat ini';
|
||||
|
||||
@override
|
||||
String get new_password => 'Kata Sandi Baru';
|
||||
|
||||
@override
|
||||
String get new_password_placeholder => 'Masukkan kata sandi baru';
|
||||
|
||||
@override
|
||||
String get new_password_not_same => 'Kata Sandi Baru Tidak Sama Dengan Kata Sandi Saat Ini';
|
||||
|
||||
@override
|
||||
String get general_information => 'Informasi Umum';
|
||||
|
||||
@override
|
||||
String get address => 'Alamat';
|
||||
|
||||
@override
|
||||
String get phone_number => 'Nomor Telepon';
|
||||
|
||||
@override
|
||||
String get currency => 'Mata Uang';
|
||||
|
||||
@override
|
||||
String get tax_rate => 'Tarif Pajak';
|
||||
|
||||
@override
|
||||
String get status_text => 'Status';
|
||||
|
||||
@override
|
||||
String get coming_soon => 'Segera Hadir';
|
||||
|
||||
@override
|
||||
String get coming_soon_desc => 'Sesuatu yang menakjubkan sedang terjadi!\nNantikan pengungkapan besarnya.';
|
||||
|
||||
@override
|
||||
String get transaction_report => 'Laporan Transaksi';
|
||||
|
||||
@override
|
||||
String get transaction_report_desc => 'Ekspor semua data transaksi dengan analitik terperinci';
|
||||
|
||||
@override
|
||||
String get invetory_report => 'Laporan Inventaris';
|
||||
|
||||
@override
|
||||
String get invetory_report_desc => 'Ekspor inventaris dan data stok dengan tren';
|
||||
|
||||
@override
|
||||
String get about_app => 'Tentang Aplikasi';
|
||||
|
||||
@override
|
||||
String get app_information => 'Informasi Aplikasi';
|
||||
|
||||
@override
|
||||
String get app_name => 'Nama Aplikasi';
|
||||
|
||||
@override
|
||||
String get build_number => 'Nomor Build';
|
||||
|
||||
@override
|
||||
String get package_name => 'Nama Paket';
|
||||
|
||||
@override
|
||||
String get device => 'Perangkat';
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
|
||||
class DateRangePickerBottomSheet {
|
||||
static Future<DateRangePickerSelectionChangedArgs?> show({
|
||||
required BuildContext context,
|
||||
@ -9,8 +11,8 @@ class DateRangePickerBottomSheet {
|
||||
DateTime? initialEndDate,
|
||||
DateTime? minDate,
|
||||
DateTime? maxDate,
|
||||
String confirmText = 'Pilih',
|
||||
String cancelText = 'Batal',
|
||||
String? confirmText,
|
||||
String? cancelText,
|
||||
Color primaryColor = Colors.blue,
|
||||
Function(DateTime? startDate, DateTime? endDate)? onChanged,
|
||||
}) async {
|
||||
@ -26,8 +28,8 @@ class DateRangePickerBottomSheet {
|
||||
initialEndDate: initialEndDate,
|
||||
minDate: minDate,
|
||||
maxDate: maxDate,
|
||||
confirmText: confirmText,
|
||||
cancelText: cancelText,
|
||||
confirmText: confirmText ?? context.lang.select,
|
||||
cancelText: cancelText ?? context.lang.cancel,
|
||||
primaryColor: primaryColor,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
@ -104,7 +106,7 @@ class _DateRangePickerBottomSheetState
|
||||
return _formatDate(range.startDate!);
|
||||
}
|
||||
}
|
||||
return 'Belum ada tanggal dipilih';
|
||||
return context.lang.no_date_selected;
|
||||
}
|
||||
|
||||
String _formatDate(DateTime date) {
|
||||
@ -187,7 +189,7 @@ class _DateRangePickerBottomSheetState
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Tanggal Terpilih:',
|
||||
'${context.lang.selected_date}:',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncfusion_flutter_datepicker/datepicker.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../bottom_sheet/date_range_bottom_sheet.dart';
|
||||
|
||||
@ -22,7 +23,7 @@ class DateRangePickerField extends StatefulWidget {
|
||||
final double height;
|
||||
|
||||
const DateRangePickerField({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.label,
|
||||
this.placeholder = 'Pilih rentang tanggal',
|
||||
this.startDate,
|
||||
@ -38,7 +39,7 @@ class DateRangePickerField extends StatefulWidget {
|
||||
this.placeholderStyle,
|
||||
this.decoration,
|
||||
this.height = 52.0,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<DateRangePickerField> createState() => _DateRangePickerFieldState();
|
||||
@ -83,7 +84,7 @@ class _DateRangePickerFieldState extends State<DateRangePickerField> {
|
||||
|
||||
final result = await DateRangePickerBottomSheet.show(
|
||||
context: context,
|
||||
title: widget.label ?? 'Pilih Rentang Tanggal',
|
||||
title: widget.label ?? context.lang.select_date_range,
|
||||
initialStartDate: widget.startDate,
|
||||
initialEndDate: widget.endDate,
|
||||
minDate: widget.minDate,
|
||||
@ -294,7 +295,7 @@ class _DateRangePickerFieldOutlinedState
|
||||
|
||||
final result = await DateRangePickerBottomSheet.show(
|
||||
context: context,
|
||||
title: widget.label ?? 'Pilih Rentang Tanggal',
|
||||
title: widget.label ?? context.lang.select_date_range,
|
||||
initialStartDate: widget.startDate,
|
||||
initialEndDate: widget.endDate,
|
||||
minDate: widget.minDate,
|
||||
@ -412,120 +413,3 @@ class _DateRangePickerFieldOutlinedState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage Example Widget
|
||||
class DateRangePickerExample extends StatefulWidget {
|
||||
@override
|
||||
_DateRangePickerExampleState createState() => _DateRangePickerExampleState();
|
||||
}
|
||||
|
||||
class _DateRangePickerExampleState extends State<DateRangePickerExample> {
|
||||
DateTime? _startDate;
|
||||
DateTime? _endDate;
|
||||
DateTime? _startDate2;
|
||||
DateTime? _endDate2;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Date Range Picker Example'),
|
||||
backgroundColor: AppColor.primary,
|
||||
foregroundColor: AppColor.white,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Default Style',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
DateRangePickerField(
|
||||
label: 'Periode Laporan',
|
||||
placeholder: 'Pilih tanggal mulai - selesai',
|
||||
startDate: _startDate,
|
||||
endDate: _endDate,
|
||||
primaryColor: AppColor.primary,
|
||||
onChanged: (start, end) {
|
||||
setState(() {
|
||||
_startDate = start;
|
||||
_endDate = end;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
Text(
|
||||
'Outlined Style',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
DateRangePickerFieldOutlined(
|
||||
label: 'Rentang Waktu',
|
||||
placeholder: 'Pilih rentang tanggal',
|
||||
startDate: _startDate2,
|
||||
endDate: _endDate2,
|
||||
primaryColor: AppColor.secondary,
|
||||
onChanged: (start, end) {
|
||||
setState(() {
|
||||
_startDate2 = start;
|
||||
_endDate2 = end;
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Display selected dates
|
||||
if (_startDate != null ||
|
||||
_endDate != null ||
|
||||
_startDate2 != null ||
|
||||
_endDate2 != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.background,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Selected Dates:',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (_startDate != null)
|
||||
Text(
|
||||
'Default: ${_startDate!} - ${_endDate ?? 'Not selected'}',
|
||||
),
|
||||
if (_startDate2 != null)
|
||||
Text(
|
||||
'Outlined: ${_startDate2!} - ${_endDate2 ?? 'Not selected'}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,9 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../components/assets/assets.gen.dart';
|
||||
|
||||
@RoutePage()
|
||||
class AboutAppPage extends StatefulWidget {
|
||||
@ -107,7 +109,7 @@ class _AboutAppPageState extends State<AboutAppPage>
|
||||
return Opacity(
|
||||
opacity: _fadeAnimation.value,
|
||||
child: Text(
|
||||
'Tentang Aplikasi',
|
||||
context.lang.about_app,
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -142,7 +144,7 @@ class _AboutAppPageState extends State<AboutAppPage>
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.white,
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.black.withOpacity(0.2),
|
||||
@ -151,10 +153,9 @@ class _AboutAppPageState extends State<AboutAppPage>
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(
|
||||
Icons.mobile_friendly,
|
||||
size: 50,
|
||||
color: AppColor.primary,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Assets.images.logo.image(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@ -247,7 +248,7 @@ class _AboutAppPageState extends State<AboutAppPage>
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Informasi Aplikasi',
|
||||
context.lang.app_information,
|
||||
style: AppStyle.h6.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary,
|
||||
@ -256,18 +257,24 @@ class _AboutAppPageState extends State<AboutAppPage>
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildInfoRow('Nama Aplikasi', packageInfo?.appName ?? 'Loading...'),
|
||||
_buildInfoRow('Versi', packageInfo?.version ?? 'Loading...'),
|
||||
_buildInfoRow(
|
||||
'Build Number',
|
||||
context.lang.app_name,
|
||||
packageInfo?.appName ?? 'Loading...',
|
||||
),
|
||||
_buildInfoRow(
|
||||
context.lang.version,
|
||||
packageInfo?.version ?? 'Loading...',
|
||||
),
|
||||
_buildInfoRow(
|
||||
context.lang.build_number,
|
||||
packageInfo?.buildNumber ?? 'Loading...',
|
||||
),
|
||||
_buildInfoRow(
|
||||
'Package Name',
|
||||
context.lang.package_name,
|
||||
packageInfo?.packageName ?? 'Loading...',
|
||||
),
|
||||
_buildInfoRow(
|
||||
'Device',
|
||||
context.lang.device,
|
||||
deviceInfo.isEmpty ? 'Loading...' : deviceInfo,
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
|
||||
@RoutePage()
|
||||
@ -114,7 +115,7 @@ class _ComingSoonPageState extends State<ComingSoonPage>
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Text(
|
||||
'Coming Soon',
|
||||
context.lang.coming_soon,
|
||||
style: AppStyle.h1.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -134,7 +135,7 @@ class _ComingSoonPageState extends State<ComingSoonPage>
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Text(
|
||||
'Something amazing is brewing!\nStay tuned for the big reveal.',
|
||||
context.lang.coming_soon_desc,
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.textWhite.withOpacity(0.9),
|
||||
height: 1.5,
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../application/customer/customer_loader/customer_loader_bloc.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../domain/customer/customer.dart';
|
||||
import '../../../injection.dart';
|
||||
@ -31,7 +32,7 @@ class CustomerPage extends StatefulWidget implements AutoRouteWrapper {
|
||||
class _CustomerPageState extends State<CustomerPage>
|
||||
with TickerProviderStateMixin {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
ScrollController _scrollController = ScrollController();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
bool _isGridView = false;
|
||||
|
||||
@override
|
||||
@ -72,7 +73,7 @@ class _CustomerPageState extends State<CustomerPage>
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Pelanggan'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.customer),
|
||||
actions: [
|
||||
ActionIconButton(onTap: () {}, icon: LineIcons.search),
|
||||
],
|
||||
@ -148,7 +149,7 @@ class _CustomerPageState extends State<CustomerPage>
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
childAspectRatio: 0.8,
|
||||
childAspectRatio: 0.65,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final customer = customers[index];
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/customer/customer.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
@ -175,78 +176,6 @@ class CustomerCard extends StatelessWidget {
|
||||
const SpaceHeight(12),
|
||||
],
|
||||
|
||||
// Status Badge
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: customer.isActive
|
||||
? AppColor.success.withOpacity(0.1)
|
||||
: AppColor.error.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: customer.isActive
|
||||
? AppColor.success.withOpacity(0.3)
|
||||
: AppColor.error.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: customer.isActive
|
||||
? AppColor.success
|
||||
: AppColor.error,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SpaceWidth(6),
|
||||
Text(
|
||||
customer.isActive ? 'Active' : 'Inactive',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: customer.isActive
|
||||
? AppColor.success
|
||||
: AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Additional info if available
|
||||
if (customer.address.isNotEmpty) ...[
|
||||
const SpaceHeight(8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_on_outlined,
|
||||
size: 14,
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
const SpaceWidth(4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
customer.address,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
// Metadata info
|
||||
if (customer.metadata.isNotEmpty && _hasRelevantMetadata()) ...[
|
||||
const SpaceHeight(8),
|
||||
@ -284,7 +213,7 @@ class CustomerCard extends StatelessWidget {
|
||||
if (customer.createdAt.isNotEmpty) ...[
|
||||
const SpaceHeight(8),
|
||||
Text(
|
||||
'Joined ${_formatDate(customer.createdAt)}',
|
||||
'${context.lang.joined} ${_formatDate(context, customer.createdAt)}',
|
||||
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@ -335,7 +264,7 @@ class CustomerCard extends StatelessWidget {
|
||||
return colors[index];
|
||||
}
|
||||
|
||||
String _formatDate(String dateStr) {
|
||||
String _formatDate(BuildContext context, String dateStr) {
|
||||
try {
|
||||
final date = DateTime.parse(dateStr);
|
||||
final now = DateTime.now();
|
||||
@ -346,13 +275,13 @@ class CustomerCard extends StatelessWidget {
|
||||
} else if (difference == 1) {
|
||||
return 'yesterday';
|
||||
} else if (difference < 30) {
|
||||
return '${difference}d ago';
|
||||
return '${difference}d ${context.lang.ago}';
|
||||
} else if (difference < 365) {
|
||||
final months = (difference / 30).floor();
|
||||
return '${months}mo ago';
|
||||
return '${months}mo ${context.lang.ago}';
|
||||
} else {
|
||||
final years = (difference / 365).floor();
|
||||
return '${years}y ago';
|
||||
return '${years}y ${context.lang.ago}';
|
||||
}
|
||||
} catch (e) {
|
||||
return dateStr;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/customer/customer.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
@ -245,7 +246,9 @@ class CustomerTile extends StatelessWidget {
|
||||
),
|
||||
const SpaceWidth(6),
|
||||
Text(
|
||||
customer.isActive ? 'Active' : 'Inactive',
|
||||
customer.isActive
|
||||
? context.lang.active
|
||||
: context.lang.inactive,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: customer.isActive
|
||||
? AppColor.success
|
||||
@ -260,7 +263,7 @@ class CustomerTile extends StatelessWidget {
|
||||
// Created date
|
||||
if (customer.createdAt.isNotEmpty)
|
||||
Text(
|
||||
'Joined ${_formatDate(customer.createdAt)}',
|
||||
'${context.lang.joined} ${_formatDate(context, customer.createdAt)}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
@ -326,7 +329,7 @@ class CustomerTile extends StatelessWidget {
|
||||
return colors[index];
|
||||
}
|
||||
|
||||
String _formatDate(String dateStr) {
|
||||
String _formatDate(BuildContext context, String dateStr) {
|
||||
try {
|
||||
final date = DateTime.parse(dateStr);
|
||||
final now = DateTime.now();
|
||||
@ -337,13 +340,13 @@ class CustomerTile extends StatelessWidget {
|
||||
} else if (difference == 1) {
|
||||
return 'yesterday';
|
||||
} else if (difference < 30) {
|
||||
return '${difference}d ago';
|
||||
return '${difference}d ${context.lang.ago}';
|
||||
} else if (difference < 365) {
|
||||
final months = (difference / 30).floor();
|
||||
return '${months}mo ago';
|
||||
return '${months}mo ${context.lang.ago}';
|
||||
} else {
|
||||
final years = (difference / 365).floor();
|
||||
return '${years}y ago';
|
||||
return '${years}y ${context.lang.ago}';
|
||||
}
|
||||
} catch (e) {
|
||||
return dateStr;
|
||||
|
||||
@ -113,7 +113,7 @@ class _DownloadReportPageState extends State<DownloadReportPage>
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Download Report'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.download_report),
|
||||
),
|
||||
|
||||
// Content
|
||||
@ -134,9 +134,8 @@ class _DownloadReportPageState extends State<DownloadReportPage>
|
||||
>(
|
||||
builder: (context, state) {
|
||||
return _ReportOptionCard(
|
||||
title: 'Transaction Report',
|
||||
subtitle:
|
||||
'Export all transaction data with detailed analytics',
|
||||
title: context.lang.transaction_report,
|
||||
subtitle: context.lang.transaction_report_desc,
|
||||
icon: Icons.receipt_long_outlined,
|
||||
gradient: const [
|
||||
AppColor.primary,
|
||||
@ -205,9 +204,8 @@ class _DownloadReportPageState extends State<DownloadReportPage>
|
||||
BlocBuilder<InventoryReportBloc, InventoryReportState>(
|
||||
builder: (context, state) {
|
||||
return _ReportOptionCard(
|
||||
title: 'Inventory Report',
|
||||
subtitle:
|
||||
'Export inventory and stock data with trends',
|
||||
title: context.lang.invetory_report,
|
||||
subtitle: context.lang.invetory_report_desc,
|
||||
icon: Icons.inventory_2_outlined,
|
||||
gradient: const [
|
||||
AppColor.secondary,
|
||||
@ -479,7 +477,7 @@ class _ReportOptionCardState extends State<_ReportOptionCard>
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Select Date Range',
|
||||
context.lang.select_date_range,
|
||||
style: AppStyle.md.copyWith(
|
||||
color: AppColor.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -489,7 +487,7 @@ class _ReportOptionCardState extends State<_ReportOptionCard>
|
||||
|
||||
// Date Range Picker Field Style
|
||||
DateRangePickerField(
|
||||
placeholder: 'Select date range',
|
||||
placeholder: context.lang.select_date_range,
|
||||
startDate: widget.startDate,
|
||||
endDate: widget.endDate,
|
||||
onChanged: widget.onDateRangeChanged,
|
||||
@ -542,7 +540,7 @@ class _ReportOptionCardState extends State<_ReportOptionCard>
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Download Report',
|
||||
context.lang.download_report,
|
||||
style: AppStyle.md.copyWith(
|
||||
color: widget.gradient.first,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@ -126,7 +126,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
elevation: 0,
|
||||
flexibleSpace: CustomAppBar(title: 'Keuangan'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.finance),
|
||||
),
|
||||
|
||||
// Header dengan filter periode
|
||||
@ -221,7 +221,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Total Pendapatan',
|
||||
title: context.lang.total_revenue,
|
||||
amount: summary.totalRevenue.currencyFormatRp,
|
||||
icon: LineIcons.arrowUp,
|
||||
color: AppColor.success,
|
||||
@ -231,7 +231,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Total Pengeluaran',
|
||||
title: context.lang.total_expenditures,
|
||||
amount: summary.totalCost.currencyFormatRp,
|
||||
icon: LineIcons.arrowDown,
|
||||
color: AppColor.error,
|
||||
@ -245,7 +245,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Keuntungan Bersih',
|
||||
title: context.lang.net_profit,
|
||||
amount: summary.netProfit.currencyFormatRp,
|
||||
icon: LineIcons.lineChart,
|
||||
color: AppColor.info,
|
||||
@ -255,7 +255,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Margin Profit',
|
||||
title: context.lang.margin_profit,
|
||||
amount: '${summary.profitabilityRatio.round()}%',
|
||||
icon: LineIcons.percent,
|
||||
color: AppColor.warning,
|
||||
@ -304,14 +304,14 @@ class _FinancePageState extends State<FinancePage>
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Analisis Produk',
|
||||
context.lang.product_analytic,
|
||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
'Lihat Semua',
|
||||
context.lang.view_all,
|
||||
style: AppStyle.sm.copyWith(color: AppColor.primary),
|
||||
),
|
||||
),
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
|
||||
@ -57,7 +58,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Analisis Cash Flow',
|
||||
context.lang.cash_flow_analysis,
|
||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
@ -78,7 +79,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Cash In',
|
||||
context.lang.cash_in,
|
||||
_formatCurrency(totalCashIn),
|
||||
LineIcons.arrowUp,
|
||||
AppColor.success,
|
||||
@ -87,7 +88,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Cash Out',
|
||||
context.lang.cash_out,
|
||||
_formatCurrency(totalCashOut),
|
||||
LineIcons.arrowDown,
|
||||
AppColor.error,
|
||||
@ -96,7 +97,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Net Flow',
|
||||
context.lang.net_flow,
|
||||
_formatCurrency(netFlow),
|
||||
LineIcons.equals,
|
||||
AppColor.info,
|
||||
@ -119,7 +120,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Grafik Cash Flow ${dailyData.length} Hari Terakhir',
|
||||
context.lang.cash_flow_chart(dailyData.length),
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -136,11 +137,11 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildChartLegend('Cash In', AppColor.success),
|
||||
_buildChartLegend(context.lang.cash_in, AppColor.success),
|
||||
const SizedBox(width: 20),
|
||||
_buildChartLegend('Cash Out', AppColor.error),
|
||||
_buildChartLegend(context.lang.cash_out, AppColor.error),
|
||||
const SizedBox(width: 20),
|
||||
_buildChartLegend('Net Flow', AppColor.info),
|
||||
_buildChartLegend(context.lang.net_flow, AppColor.info),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@ -51,7 +51,7 @@ class FinanceCategory extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Kategori Penjualan',
|
||||
context.lang.sales_category,
|
||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
@ -60,10 +60,11 @@ class FinanceCategory extends StatelessWidget {
|
||||
|
||||
// Show empty state if no categories
|
||||
if (categories.isEmpty)
|
||||
_buildEmptyState()
|
||||
_buildEmptyState(context)
|
||||
else
|
||||
...sortedCategories.asMap().entries.map(
|
||||
(entry) => _buildCategoryItem(
|
||||
context,
|
||||
entry.value,
|
||||
_calculatePercentage(entry.value.totalRevenue, totalRevenue),
|
||||
_getCategoryColor(entry.key),
|
||||
@ -75,6 +76,7 @@ class FinanceCategory extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildCategoryItem(
|
||||
BuildContext context,
|
||||
CategoryAnalyticItem category,
|
||||
double percentage,
|
||||
Color color,
|
||||
@ -111,7 +113,7 @@ class FinanceCategory extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'${category.productCount} produk • ${category.orderCount} pesanan',
|
||||
'${category.productCount} ${context.lang.product} • ${category.orderCount} ${context.lang.orders}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
@ -134,7 +136,7 @@ class FinanceCategory extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${NumberFormat('#,###', 'id_ID').format(category.totalQuantity)} unit',
|
||||
'${NumberFormat('#,###', 'id_ID').format(category.totalQuantity)} ${context.lang.unit}',
|
||||
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
],
|
||||
@ -161,10 +163,10 @@ class FinanceCategory extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
return EmptyWidget(
|
||||
title: 'Belum ada data kategori',
|
||||
message: 'Data kategori penjualan akan muncul di sini',
|
||||
title: context.lang.category_no_data,
|
||||
message: context.lang.category_no_data_desc,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -81,21 +81,21 @@ class ProfitLossProduct extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Pendapatan',
|
||||
context.lang.revenue,
|
||||
product.revenue.currencyFormatRp,
|
||||
AppColor.success,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Biaya',
|
||||
context.lang.cost,
|
||||
product.cost.currencyFormatRp,
|
||||
AppColor.error,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Laba Kotor',
|
||||
context.lang.gross_profit,
|
||||
product.grossProfit.currencyFormatRp,
|
||||
AppColor.info,
|
||||
),
|
||||
@ -109,14 +109,14 @@ class ProfitLossProduct extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Harga Rata-rata',
|
||||
context.lang.average_price,
|
||||
product.averagePrice.currencyFormatRp,
|
||||
AppColor.textSecondary,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Laba per Unit',
|
||||
context.lang.profit_per_unit,
|
||||
product.profitPerUnit.currencyFormatRp,
|
||||
AppColor.primary,
|
||||
),
|
||||
|
||||
@ -46,7 +46,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Detail Profit & Loss',
|
||||
context.lang.profit_loss_detail,
|
||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
@ -55,7 +55,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Total Revenue (Penjualan Kotor)
|
||||
_buildPLItem(
|
||||
'Penjualan Kotor',
|
||||
context.lang.gross_sales,
|
||||
data.totalRevenue.currencyFormatRp,
|
||||
AppColor.success,
|
||||
true,
|
||||
@ -63,7 +63,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Discount (Diskon & Retur)
|
||||
_buildPLItem(
|
||||
'Diskon & Retur',
|
||||
'${context.lang.discount} & ${context.lang.return_text}',
|
||||
'- ${data.totalDiscount.currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
@ -73,7 +73,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Net Sales (Penjualan Bersih = Total Revenue - Discount)
|
||||
_buildPLItem(
|
||||
'Penjualan Bersih',
|
||||
context.lang.net_sales,
|
||||
(data.totalRevenue - data.totalDiscount).currencyFormatRp,
|
||||
AppColor.textPrimary,
|
||||
true,
|
||||
@ -84,7 +84,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Cost of Goods Sold (HPP)
|
||||
_buildPLItem(
|
||||
'HPP (Harga Pokok Penjualan)',
|
||||
'${context.lang.cogs} (${context.lang.cost_of_goods_sold})',
|
||||
'- ${data.totalCost.currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
@ -94,7 +94,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Gross Profit (Laba Kotor)
|
||||
_buildPLItem(
|
||||
'Laba Kotor',
|
||||
context.lang.gross_profit,
|
||||
data.grossProfit.currencyFormatRp,
|
||||
AppColor.success,
|
||||
true,
|
||||
@ -107,7 +107,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Operational Cost (Biaya Operasional) - calculated as difference
|
||||
_buildPLItem(
|
||||
'Biaya Operasional',
|
||||
context.lang.operating_costs,
|
||||
'- ${_calculateOperationalCost().currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
@ -117,7 +117,7 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
|
||||
// Net Profit (Laba Bersih)
|
||||
_buildPLItem(
|
||||
'Laba Bersih',
|
||||
context.lang.net_profit,
|
||||
data.netProfit.currencyFormatRp,
|
||||
AppColor.primary,
|
||||
true,
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'feature_tile.dart';
|
||||
@ -36,25 +37,25 @@ class HomeFeature extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HomeFeatureTile(
|
||||
title: 'Penjualan',
|
||||
title: context.lang.sales,
|
||||
color: const Color(0xFF4CAF50),
|
||||
icon: LineIcons.receipt,
|
||||
onTap: () => context.router.push(SalesRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Pembelian',
|
||||
title: context.lang.purchase,
|
||||
color: const Color(0xFF2196F3),
|
||||
icon: LineIcons.shoppingCart,
|
||||
onTap: () => context.router.push(PurchaseRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Keuangan',
|
||||
title: context.lang.finance,
|
||||
color: const Color(0xFF8BC34A),
|
||||
icon: LineIcons.moneyCheck,
|
||||
onTap: () => context.router.push(FinanceRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Produk',
|
||||
title: context.lang.product,
|
||||
color: const Color(0xFFFF9800),
|
||||
icon: LineIcons.box,
|
||||
onTap: () => context.router.push(ProductAnalyticRoute()),
|
||||
@ -66,25 +67,25 @@ class HomeFeature extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HomeFeatureTile(
|
||||
title: 'Form',
|
||||
title: context.lang.form,
|
||||
color: const Color(0xFFE91E63),
|
||||
icon: LineIcons.fileAlt,
|
||||
onTap: () => context.router.push(DailyTasksFormRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Jadwal',
|
||||
title: context.lang.schedule,
|
||||
color: const Color(0xFF9C27B0),
|
||||
icon: LineIcons.calendar,
|
||||
onTap: () => context.router.push(ScheduleRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Inventaris',
|
||||
title: context.lang.inventory,
|
||||
color: const Color(0xFF00BCD4),
|
||||
icon: LineIcons.archive,
|
||||
onTap: () => context.router.push(InventoryRoute()),
|
||||
),
|
||||
HomeFeatureTile(
|
||||
title: 'Pelanggan',
|
||||
title: context.lang.customer,
|
||||
color: const Color(0xFFFF5722),
|
||||
icon: LineIcons.userPlus,
|
||||
onTap: () => context.router.push(CustomerRoute()),
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
@ -21,30 +22,30 @@ class HomeStats extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HomeTitle(title: 'Ringkasan Hari Ini'),
|
||||
HomeTitle(title: context.lang.today_summary),
|
||||
const SpaceHeight(20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: HomeStatsTile(
|
||||
title: 'Pesanan',
|
||||
title: context.lang.order,
|
||||
value: overview.totalOrders.toString(),
|
||||
icon: Icons.receipt_long_rounded,
|
||||
color: AppColor.info,
|
||||
subtitle: 'Hari ini',
|
||||
subtitle: context.lang.today,
|
||||
),
|
||||
),
|
||||
|
||||
const SpaceWidth(16),
|
||||
Expanded(
|
||||
child: HomeStatsTile(
|
||||
title: 'Pelanggan Baru',
|
||||
title: context.lang.new_customer,
|
||||
value: overview.totalCustomers.toString(),
|
||||
icon: Icons.person_add_outlined,
|
||||
color: AppColor.primary,
|
||||
subtitle: overview.totalCustomers < 1
|
||||
? 'Hari ini'
|
||||
: 'bertambah',
|
||||
? context.lang.today
|
||||
: context.lang.increase,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -54,21 +55,21 @@ class HomeStats extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: HomeStatsTile(
|
||||
title: 'Refund',
|
||||
title: context.lang.refund,
|
||||
value: overview.refundedOrders.toString(),
|
||||
icon: LineIcons.alternateExchange,
|
||||
color: AppColor.warning,
|
||||
subtitle: 'Hari ini',
|
||||
subtitle: context.lang.today,
|
||||
),
|
||||
),
|
||||
const SpaceWidth(16),
|
||||
Expanded(
|
||||
child: HomeStatsTile(
|
||||
title: 'Void',
|
||||
title: context.lang.void_text,
|
||||
value: overview.voidedOrders.toString(),
|
||||
icon: Icons.cancel_rounded,
|
||||
color: AppColor.error,
|
||||
subtitle: 'Hari ini',
|
||||
subtitle: context.lang.today,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
@ -19,12 +20,13 @@ class HomeTopProduct extends StatelessWidget {
|
||||
).copyWith(bottom: 0),
|
||||
child: Column(
|
||||
children: [
|
||||
HomeTitle(title: 'Product Terlaris Hari Ini'),
|
||||
HomeTitle(title: context.lang.today_top_product),
|
||||
SpaceHeight(20),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: products.length,
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (context, index) {
|
||||
return HomeTopProductTile(
|
||||
product: products[index],
|
||||
|
||||
@ -44,7 +44,7 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
// Header Row - Ranking dan Revenue
|
||||
Row(
|
||||
children: [
|
||||
_buildRankingBadge(),
|
||||
_buildRankingBadge(context),
|
||||
const Spacer(),
|
||||
_buildRevenueDisplay(),
|
||||
],
|
||||
@ -71,7 +71,7 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Metrics dalam Grid 2x2
|
||||
_buildMetricsGrid(),
|
||||
_buildMetricsGrid(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -80,7 +80,7 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRankingBadge() {
|
||||
Widget _buildRankingBadge(BuildContext context) {
|
||||
Color badgeColor;
|
||||
IconData icon;
|
||||
|
||||
@ -115,7 +115,7 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
Icon(icon, color: badgeColor, size: 16),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Rank #$ranking',
|
||||
'${context.lang.rank} #$ranking',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: badgeColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -157,7 +157,7 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMetricsGrid() {
|
||||
Widget _buildMetricsGrid(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -165,14 +165,14 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
children: [
|
||||
_buildMetricCard(
|
||||
icon: Icons.shopping_cart_outlined,
|
||||
label: 'Quantity Sold',
|
||||
label: context.lang.quantity_sold,
|
||||
value: product.quantitySold.toString(),
|
||||
color: AppColor.info,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMetricCard(
|
||||
icon: Icons.attach_money,
|
||||
label: 'Average Price',
|
||||
label: context.lang.average_price,
|
||||
value: product.averagePrice.round().currencyFormatRp,
|
||||
color: AppColor.success,
|
||||
),
|
||||
@ -185,14 +185,14 @@ class HomeTopProductTile extends StatelessWidget {
|
||||
children: [
|
||||
_buildMetricCard(
|
||||
icon: Icons.receipt_outlined,
|
||||
label: 'Total Orders',
|
||||
label: context.lang.total_orders,
|
||||
value: product.orderCount.toString(),
|
||||
color: AppColor.warning,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMetricCard(
|
||||
icon: Icons.trending_up,
|
||||
label: 'Performance',
|
||||
label: context.lang.perfomance,
|
||||
value: 'Top $ranking',
|
||||
color: AppColor.primary,
|
||||
),
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../application/analytic/inventory_analytic_loader/inventory_analytic_loader_bloc.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../domain/analytic/analytic.dart';
|
||||
import '../../../injection.dart';
|
||||
@ -95,11 +96,11 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
String getStatusText(String status) {
|
||||
switch (status) {
|
||||
case 'available':
|
||||
return 'Tersedia';
|
||||
return context.lang.available;
|
||||
case 'low_stock':
|
||||
return 'Stok Rendah';
|
||||
return context.lang.low_stock;
|
||||
case 'out_of_stock':
|
||||
return 'Habis';
|
||||
return context.lang.out_of_stock;
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
@ -189,7 +190,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
child: const Row(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
@ -199,7 +200,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
size: 16,
|
||||
),
|
||||
SizedBox(width: 6),
|
||||
Text('Produk'),
|
||||
Text(context.lang.product),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -210,7 +211,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
child: const Row(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
@ -220,7 +221,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
size: 16,
|
||||
),
|
||||
SizedBox(width: 6),
|
||||
Text('Bahan'),
|
||||
Text(context.lang.ingredients),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -254,7 +255,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Inventaris'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.inventory),
|
||||
);
|
||||
}
|
||||
|
||||
@ -308,7 +309,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Total Produk',
|
||||
context.lang.total_products,
|
||||
inventory.totalProducts.toString(),
|
||||
Icons.inventory_2_rounded,
|
||||
AppColor.primary,
|
||||
@ -317,7 +318,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Produk Terjual',
|
||||
context.lang.total_sold,
|
||||
inventory.totalSoldProducts.toString(),
|
||||
Icons.check_circle_rounded,
|
||||
AppColor.success,
|
||||
@ -330,7 +331,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Stok Rendah',
|
||||
context.lang.low_stock,
|
||||
inventory.lowStockProducts.toString(),
|
||||
Icons.warning_rounded,
|
||||
AppColor.warning,
|
||||
@ -339,7 +340,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Stok Kosong',
|
||||
context.lang.zero_stock,
|
||||
inventory.zeroStockProducts.toString(),
|
||||
Icons.error_rounded,
|
||||
AppColor.error,
|
||||
@ -361,7 +362,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Total Bahan',
|
||||
context.lang.total_ingredients,
|
||||
inventory.totalIngredients.toString(),
|
||||
Icons.restaurant_menu_rounded,
|
||||
AppColor.primary,
|
||||
@ -370,7 +371,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Bahan Terjual',
|
||||
context.lang.total_sold,
|
||||
inventory.totalSoldIngredients.toString(),
|
||||
Icons.check_circle_rounded,
|
||||
AppColor.success,
|
||||
@ -383,7 +384,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Stok Kurang',
|
||||
context.lang.low_stock,
|
||||
inventory.lowStockIngredients.toString(),
|
||||
Icons.warning_rounded,
|
||||
AppColor.warning,
|
||||
@ -392,7 +393,7 @@ class _InventoryPageState extends State<InventoryPage>
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: _buildStatCard(
|
||||
'Habis',
|
||||
context.lang.zero_stock,
|
||||
inventory.zeroStockIngredients.toString(),
|
||||
Icons.error_rounded,
|
||||
AppColor.error,
|
||||
|
||||
@ -182,7 +182,11 @@ class InventoryProductTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
_getStatusText(),
|
||||
item.isZeroStock
|
||||
? context.lang.out_of_stock
|
||||
: item.isLowStock
|
||||
? context.lang.low_stock
|
||||
: context.lang.available,
|
||||
style: AppStyle.xs.copyWith(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
@ -202,7 +206,7 @@ class InventoryProductTile extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildInfoItem(
|
||||
LineIcons.boxes,
|
||||
'Stok',
|
||||
context.lang.stock,
|
||||
'${NumberFormat('#,###', 'id_ID').format(item.quantity)} pcs',
|
||||
_getQuantityColor(),
|
||||
),
|
||||
@ -212,7 +216,7 @@ class InventoryProductTile extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildInfoItem(
|
||||
LineIcons.dollarSign,
|
||||
'Nilai',
|
||||
context.lang.value_text,
|
||||
_formatCurrencyShort(item.totalValue),
|
||||
AppColor.info,
|
||||
),
|
||||
@ -259,14 +263,19 @@ class InventoryProductTile extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Stok Menipis',
|
||||
context.lang.low_stock,
|
||||
style: AppStyle.sm.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColor.warning,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Segera reorder minimal ${NumberFormat('#,###', 'id_ID').format(item.reorderLevel)} pcs',
|
||||
context.lang.low_stock_desc(
|
||||
NumberFormat(
|
||||
'#,###',
|
||||
'id_ID',
|
||||
).format(item.reorderLevel),
|
||||
),
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -301,14 +310,14 @@ class InventoryProductTile extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Stok Habis',
|
||||
context.lang.out_of_stock,
|
||||
style: AppStyle.sm.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColor.error,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Produk tidak tersedia untuk dijual',
|
||||
context.lang.out_of_stock_desc,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -330,7 +339,7 @@ class InventoryProductTile extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildMovementInfo(
|
||||
LineIcons.arrowUp,
|
||||
'Masuk',
|
||||
context.lang.in_text,
|
||||
'${NumberFormat('#,###', 'id_ID').format(item.totalIn)} pcs',
|
||||
AppColor.success,
|
||||
),
|
||||
@ -341,7 +350,7 @@ class InventoryProductTile extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildMovementInfo(
|
||||
LineIcons.arrowDown,
|
||||
'Keluar',
|
||||
context.lang.out_text,
|
||||
'${NumberFormat('#,###', 'id_ID').format(item.totalOut)} pcs',
|
||||
AppColor.error,
|
||||
),
|
||||
@ -476,12 +485,6 @@ class InventoryProductTile extends StatelessWidget {
|
||||
return AppColor.textPrimary;
|
||||
}
|
||||
|
||||
String _getStatusText() {
|
||||
if (item.isZeroStock) return 'HABIS';
|
||||
if (item.isLowStock) return 'MINIM';
|
||||
return 'TERSEDIA';
|
||||
}
|
||||
|
||||
IconData _getCategoryIcon() {
|
||||
final category = item.categoryName.toLowerCase();
|
||||
if (category.contains('elektronik') || category.contains('gadget')) {
|
||||
|
||||
@ -33,8 +33,8 @@ class _MainBottomNavbarState extends State<MainBottomNavbar> {
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: LineIcon(LineIcons.moneyBill),
|
||||
label: 'Order',
|
||||
tooltip: 'Order',
|
||||
label: context.lang.order,
|
||||
tooltip: context.lang.order,
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: LineIcon(LineIcons.barChart),
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'dart:math' show cos, sin;
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/order/order.dart';
|
||||
import '../../../components/appbar/appbar.dart';
|
||||
@ -22,33 +23,33 @@ class OrderDetailPage extends StatelessWidget {
|
||||
expandedHeight: 120,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: const CustomAppBar(title: "Detail Pesanan"),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.order_details),
|
||||
elevation: 0,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
// Order Status Card
|
||||
_buildOrderStatusCard(),
|
||||
_buildOrderStatusCard(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Table Visual Card (for dine-in)
|
||||
if (_isDineIn()) _buildTableVisualCard(),
|
||||
if (_isDineIn()) _buildTableVisualCard(context),
|
||||
|
||||
// Order Info Card
|
||||
_buildOrderInfoCard(),
|
||||
_buildOrderInfoCard(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Order Items Section
|
||||
_buildOrderItemsSection(),
|
||||
_buildOrderItemsSection(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Payment Summary Card
|
||||
_buildPaymentSummaryCard(),
|
||||
_buildPaymentSummaryCard(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Payment Methods Section
|
||||
_buildPaymentMethodsSection(),
|
||||
_buildPaymentMethodsSection(context),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
@ -58,7 +59,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderStatusCard() {
|
||||
Widget _buildOrderStatusCard(BuildContext context) {
|
||||
Color statusColor = _getStatusColor(order.status);
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
@ -123,7 +124,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Order Number',
|
||||
context.lang.order_number,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -157,7 +158,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Status Pesanan',
|
||||
context.lang.order_status,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -197,7 +198,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Total Amount',
|
||||
context.lang.total_amount,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -222,7 +223,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableVisualCard() {
|
||||
Widget _buildTableVisualCard(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
height: 220, // Increased height
|
||||
@ -284,14 +285,14 @@ class OrderDetailPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Makan di Tempat',
|
||||
context.lang.dine_in,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.white.withOpacity(0.9),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Dine In Experience',
|
||||
context.lang.dine_in_experience,
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -347,7 +348,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'MEJA',
|
||||
context.lang.table,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.primary.withOpacity(
|
||||
0.7,
|
||||
@ -417,7 +418,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'${order.orderItems.length} Items',
|
||||
'${order.orderItems.length} ${context.lang.items}',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -470,7 +471,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderInfoCard() {
|
||||
Widget _buildOrderInfoCard(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
@ -517,7 +518,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Informasi Pesanan',
|
||||
context.lang.order_information,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary,
|
||||
@ -527,24 +528,28 @@ class OrderDetailPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (_shouldShowTableNumber())
|
||||
_buildInfoRow(Icons.table_restaurant, 'Meja', order.tableNumber),
|
||||
_buildInfoRow(
|
||||
Icons.table_restaurant,
|
||||
context.lang.table,
|
||||
order.tableNumber,
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.delivery_dining,
|
||||
'Tipe Pesanan',
|
||||
context.lang.order_type,
|
||||
order.orderType,
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.payment,
|
||||
'Status Pembayaran',
|
||||
context.lang.payment_status,
|
||||
order.paymentStatus,
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.access_time,
|
||||
'Dibuat',
|
||||
context.lang.created,
|
||||
_formatDateTime(order.createdAt),
|
||||
),
|
||||
if (order.notes.isNotEmpty)
|
||||
_buildInfoRow(Icons.note, 'Catatan', order.notes),
|
||||
_buildInfoRow(Icons.note, context.lang.note, order.notes),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -590,7 +595,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderItemsSection() {
|
||||
Widget _buildOrderItemsSection(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
@ -612,7 +617,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'Item Pesanan',
|
||||
context.lang.order_item,
|
||||
style: AppStyle.lg.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
@ -629,7 +634,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'${order.orderItems.length} Item',
|
||||
'${order.orderItems.length} ${context.lang.item}',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -647,7 +652,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
separatorBuilder: (context, index) => const Divider(height: 24),
|
||||
itemBuilder: (context, index) {
|
||||
final item = order.orderItems[index];
|
||||
return _buildOrderItemCard(item);
|
||||
return _buildOrderItemCard(context, item);
|
||||
},
|
||||
),
|
||||
],
|
||||
@ -655,9 +660,9 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderItemCard(OrderItem item) {
|
||||
Widget _buildOrderItemCard(BuildContext context, OrderItem item) {
|
||||
// Assume we add status to OrderItem model or determine from order status
|
||||
String itemStatus = _getItemStatus(item);
|
||||
String itemStatus = _getItemStatus(context, item);
|
||||
Color itemStatusColor = _getItemStatusColor(itemStatus);
|
||||
|
||||
return Container(
|
||||
@ -786,7 +791,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
'Diskon: -Rp ${_formatCurrency(item.discountAmount)}',
|
||||
'${context.lang.discount}: -Rp ${_formatCurrency(item.discountAmount)}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -811,7 +816,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Total Item',
|
||||
context.lang.total_item,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -845,7 +850,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentSummaryCard() {
|
||||
Widget _buildPaymentSummaryCard(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
@ -866,26 +871,38 @@ class OrderDetailPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Ringkasan Pembayaran',
|
||||
context.lang.payment_summary,
|
||||
style: AppStyle.lg.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildSummaryRow('Subtotal', order.subtotal),
|
||||
_buildSummaryRow(context.lang.subtotal, order.subtotal),
|
||||
if (order.discountAmount > 0)
|
||||
_buildSummaryRow(
|
||||
'Diskon',
|
||||
context.lang.discount,
|
||||
-order.discountAmount,
|
||||
isDiscount: true,
|
||||
),
|
||||
_buildSummaryRow('Pajak', order.taxAmount),
|
||||
_buildSummaryRow(context.lang.tax, order.taxAmount),
|
||||
const Divider(height: 24),
|
||||
_buildSummaryRow('Total', order.totalAmount, isTotal: true),
|
||||
_buildSummaryRow('Dibayar', order.totalPaid, isSuccess: true),
|
||||
_buildSummaryRow(
|
||||
context.lang.total,
|
||||
order.totalAmount,
|
||||
isTotal: true,
|
||||
),
|
||||
_buildSummaryRow(
|
||||
context.lang.paid,
|
||||
order.totalPaid,
|
||||
isSuccess: true,
|
||||
),
|
||||
if (order.remainingAmount > 0)
|
||||
_buildSummaryRow('Sisa', order.remainingAmount, isError: true),
|
||||
_buildSummaryRow(
|
||||
context.lang.remaining,
|
||||
order.remainingAmount,
|
||||
isError: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -930,7 +947,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentMethodsSection() {
|
||||
Widget _buildPaymentMethodsSection(BuildContext context) {
|
||||
if (order.payments.isEmpty) return const SizedBox();
|
||||
|
||||
return Container(
|
||||
@ -952,7 +969,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
|
||||
child: Text(
|
||||
'Metode Pembayaran',
|
||||
context.lang.payment_method,
|
||||
style: AppStyle.lg.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
@ -1159,7 +1176,7 @@ class OrderDetailPage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
String _getItemStatus(OrderItem item) {
|
||||
String _getItemStatus(BuildContext context, OrderItem item) {
|
||||
// Logic untuk menentukan status item berdasarkan:
|
||||
// 1. Status order secara keseluruhan
|
||||
// 2. Jika ada field status di OrderItem model
|
||||
@ -1167,26 +1184,24 @@ class OrderDetailPage extends StatelessWidget {
|
||||
|
||||
switch (order.status.toLowerCase()) {
|
||||
case 'completed':
|
||||
return 'Selesai';
|
||||
return context.lang.completed;
|
||||
case 'pending':
|
||||
return 'Menunggu';
|
||||
return context.lang.pending;
|
||||
case 'cancelled':
|
||||
return 'Dibatalkan';
|
||||
return context.lang.void_text;
|
||||
default:
|
||||
return 'Menunggu';
|
||||
return context.lang.pending;
|
||||
}
|
||||
}
|
||||
|
||||
Color _getItemStatusColor(String status) {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'selesai':
|
||||
case 'completed':
|
||||
return AppColor.success;
|
||||
case 'diproses':
|
||||
case 'pending':
|
||||
return AppColor.warning;
|
||||
case 'dibatalkan':
|
||||
case 'cancelled':
|
||||
return AppColor.error;
|
||||
case 'siap':
|
||||
return AppColor.info;
|
||||
default:
|
||||
return AppColor.primary;
|
||||
}
|
||||
|
||||
@ -3,15 +3,13 @@ import 'dart:developer';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
import '../../../../application/order/order_loader/order_loader_bloc.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../injection.dart';
|
||||
import '../../../components/appbar/appbar.dart';
|
||||
import '../../../components/button/button.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
import '../../../components/widgets/empty_widget.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'widgets/filter_header_delegate.dart';
|
||||
@ -224,11 +222,10 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
centerTitle: false,
|
||||
flexibleSpace: CustomAppBar(title: 'Order', isBack: false),
|
||||
actions: [
|
||||
ActionIconButton(onTap: () {}, icon: LineIcons.filter),
|
||||
SpaceWidth(8),
|
||||
],
|
||||
flexibleSpace: CustomAppBar(
|
||||
title: context.lang.orders,
|
||||
isBack: false,
|
||||
),
|
||||
),
|
||||
|
||||
// Pinned Filter Section
|
||||
@ -285,7 +282,7 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${state.orders.length} ${state.status.toLowerCase()} order${state.orders.length != 1 ? 's' : ''}',
|
||||
'${state.orders.length} ${state.status.toLowerCase()} ${context.lang.orders}',
|
||||
style: TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontSize: 14,
|
||||
@ -300,9 +297,10 @@ class _OrderPageState extends State<OrderPage> with TickerProviderStateMixin {
|
||||
_buildShimmerList()
|
||||
else if (state.orders.isEmpty)
|
||||
EmptyWidget(
|
||||
title: 'Order',
|
||||
message:
|
||||
'No ${state.status.toLowerCase()} orders found',
|
||||
title: context.lang.order,
|
||||
message: context.lang.no_order_with_status(
|
||||
state.status.toLowerCase(),
|
||||
),
|
||||
)
|
||||
else
|
||||
ListView.builder(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../common/extension/extension.dart';
|
||||
import '../../../../components/field/date_range_picker_field.dart';
|
||||
import 'status_tile.dart';
|
||||
|
||||
@ -36,7 +37,6 @@ class FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
double shrinkOffset,
|
||||
bool overlapsContent,
|
||||
) {
|
||||
print('FilterHeaderDelegate build called'); // Debug log
|
||||
return Container(
|
||||
color: backgroundColor,
|
||||
padding: padding,
|
||||
@ -50,9 +50,6 @@ class FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
onChanged: (start, end) {
|
||||
print(
|
||||
'onChanged called in FilterHeaderDelegate: $start - $end',
|
||||
); // Debug log
|
||||
onDateChanged(start, end);
|
||||
},
|
||||
),
|
||||
@ -70,7 +67,11 @@ class FilterHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
right: index < filterOptions.length - 1 ? 8 : 0,
|
||||
),
|
||||
child: OrderStatusTile(
|
||||
label: option,
|
||||
label: option == "All"
|
||||
? context.lang.all
|
||||
: option == "Completed"
|
||||
? context.lang.completed
|
||||
: context.lang.pending,
|
||||
isSelected: option == selectedFilter,
|
||||
onSelected: (isSelected) {
|
||||
if (isSelected) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../../common/extension/extension.dart';
|
||||
import '../../../../../common/theme/theme.dart';
|
||||
import '../../../../../domain/order/order.dart';
|
||||
|
||||
@ -46,19 +47,19 @@ class OrderTile extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header Row
|
||||
_buildHeaderRow(),
|
||||
_buildHeaderRow(context),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Order Info
|
||||
_buildOrderInfo(),
|
||||
_buildOrderInfo(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Amount Section
|
||||
_buildAmountSection(),
|
||||
_buildAmountSection(context),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Footer with Actions
|
||||
_buildFooterActions(),
|
||||
_buildFooterActions(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -67,7 +68,7 @@ class OrderTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderRow() {
|
||||
Widget _buildHeaderRow(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@ -94,12 +95,12 @@ class OrderTile extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildStatusChip(),
|
||||
_buildStatusChip(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusChip() {
|
||||
Widget _buildStatusChip(BuildContext context) {
|
||||
Color statusColor;
|
||||
String statusText;
|
||||
IconData statusIcon;
|
||||
@ -107,27 +108,34 @@ class OrderTile extends StatelessWidget {
|
||||
// Check isVoid and isRefund first for display
|
||||
if (order.isVoid) {
|
||||
statusColor = AppColor.error;
|
||||
statusText = 'Void';
|
||||
statusText = context.lang.void_text;
|
||||
statusIcon = Icons.block;
|
||||
} else if (order.isRefund) {
|
||||
statusColor = AppColor.info;
|
||||
statusText = 'Refunded';
|
||||
statusText = context.lang.refund;
|
||||
statusIcon = Icons.undo;
|
||||
} else {
|
||||
// Handle status values (only pending and completed)
|
||||
switch (order.status.toLowerCase()) {
|
||||
case 'completed':
|
||||
statusColor = AppColor.success;
|
||||
statusText = context.lang.completed;
|
||||
statusIcon = Icons.check_circle;
|
||||
case 'paid':
|
||||
case 'finished':
|
||||
statusColor = AppColor.success;
|
||||
statusText = 'Completed';
|
||||
statusText = context.lang.completed;
|
||||
statusIcon = Icons.check_circle;
|
||||
break;
|
||||
case 'pending':
|
||||
statusColor = AppColor.warning;
|
||||
statusText = context.lang.pending;
|
||||
statusIcon = Icons.schedule;
|
||||
break;
|
||||
case 'waiting':
|
||||
case 'processing':
|
||||
statusColor = AppColor.warning;
|
||||
statusText = 'Pending';
|
||||
statusText = context.lang.pending;
|
||||
statusIcon = Icons.schedule;
|
||||
break;
|
||||
default:
|
||||
@ -163,7 +171,7 @@ class OrderTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderInfo() {
|
||||
Widget _buildOrderInfo(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -177,7 +185,7 @@ class OrderTile extends StatelessWidget {
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_getOrderInfoText(),
|
||||
_getOrderInfoText(context),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -198,7 +206,7 @@ class OrderTile extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'${order.orderItems.length} items',
|
||||
'${order.orderItems.length} ${context.lang.items}',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppColor.textSecondary,
|
||||
@ -239,7 +247,7 @@ class OrderTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAmountSection() {
|
||||
Widget _buildAmountSection(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
@ -264,8 +272,8 @@ class OrderTile extends StatelessWidget {
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Total Amount',
|
||||
Text(
|
||||
context.lang.total_amount,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColor.textWhite,
|
||||
@ -274,7 +282,7 @@ class OrderTile extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Rp ${NumberFormat('#,###').format(order.totalAmount)}',
|
||||
order.totalAmount.currencyFormatRp,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -284,7 +292,7 @@ class OrderTile extends StatelessWidget {
|
||||
if (order.remainingAmount > 0) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Remaining: Rp ${NumberFormat('#,###').format(order.remainingAmount)}',
|
||||
'${context.lang.remaining}: ${order.remainingAmount.currencyFormatRp}',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColor.textWhite,
|
||||
@ -311,7 +319,7 @@ class OrderTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFooterActions() {
|
||||
Widget _buildFooterActions(BuildContext context) {
|
||||
// Don't show anything if order is void or refunded
|
||||
if (order.isVoid || order.isRefund) {
|
||||
return const SizedBox.shrink();
|
||||
@ -326,7 +334,7 @@ class OrderTile extends StatelessWidget {
|
||||
if (order.payments.isNotEmpty) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Payment: ${_getPaymentMethods()}',
|
||||
'${context.lang.payment}: ${_getPaymentMethods()}',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: AppColor.textLight,
|
||||
@ -337,59 +345,10 @@ class OrderTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (order.status.toLowerCase() == 'completed') ...[
|
||||
_buildActionButton(
|
||||
icon: Icons.print,
|
||||
label: 'Print',
|
||||
onPressed: onPrint,
|
||||
color: AppColor.info,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButton(
|
||||
icon: Icons.undo,
|
||||
label: 'Refund',
|
||||
onPressed: onRefund,
|
||||
color: AppColor.warning,
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required VoidCallback? onPressed,
|
||||
required Color color,
|
||||
}) {
|
||||
return Material(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 16, color: color),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
IconData _getOrderInfoIcon() {
|
||||
switch (order.orderType.toLowerCase()) {
|
||||
case 'dine in':
|
||||
@ -406,11 +365,11 @@ class OrderTile extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
String _getOrderInfoText() {
|
||||
String _getOrderInfoText(BuildContext context) {
|
||||
switch (order.orderType.toLowerCase()) {
|
||||
case 'dine in':
|
||||
case 'dine_in':
|
||||
return 'Table ${order.tableNumber.isNotEmpty ? order.tableNumber : 'N/A'}';
|
||||
return '${context.lang.table} ${order.tableNumber.isNotEmpty ? order.tableNumber : 'N/A'}';
|
||||
case 'takeaway':
|
||||
case 'take_away':
|
||||
case 'pickup':
|
||||
@ -419,8 +378,8 @@ class OrderTile extends StatelessWidget {
|
||||
return 'Delivery Order';
|
||||
default:
|
||||
return order.tableNumber.isNotEmpty
|
||||
? 'Table ${order.tableNumber}'
|
||||
: 'Order ${order.orderNumber}';
|
||||
? '${context.lang.table} ${order.tableNumber}'
|
||||
: context.lang.order;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,10 +3,12 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../application/outlet/current_outlet_loader/current_outlet_loader_bloc.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../domain/outlet/outlet.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
import '../../components/spacer/spacer.dart';
|
||||
|
||||
// Outlet Information Page
|
||||
@RoutePage()
|
||||
@ -79,14 +81,14 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
expandedHeight: 120.0,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
flexibleSpace: CustomAppBar(title: 'Outlet Information'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.outlet_information),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: _buildContent(),
|
||||
child: _buildContent(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -95,7 +97,7 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent() {
|
||||
Widget _buildContent(BuildContext context) {
|
||||
return BlocBuilder<CurrentOutletLoaderBloc, CurrentOutletLoaderState>(
|
||||
builder: (context, state) {
|
||||
return Padding(
|
||||
@ -105,15 +107,11 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
children: [
|
||||
_buildHeaderCard(state.outlet),
|
||||
const SizedBox(height: 20),
|
||||
_buildInfoSection(state.outlet),
|
||||
_buildInfoSection(context, state.outlet),
|
||||
const SizedBox(height: 20),
|
||||
_buildContactSection(state.outlet),
|
||||
_buildBusinessSection(context, state.outlet),
|
||||
const SizedBox(height: 20),
|
||||
_buildBusinessSection(state.outlet),
|
||||
const SizedBox(height: 20),
|
||||
_buildStatusSection(state.outlet),
|
||||
const SizedBox(height: 20),
|
||||
_buildTimestampSection(state.outlet),
|
||||
_buildStatusSection(context, state.outlet),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -190,62 +188,65 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoSection(Outlet outlet) {
|
||||
Widget _buildInfoSection(BuildContext context, Outlet outlet) {
|
||||
return _buildAnimatedCard(
|
||||
delay: 200,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('General Information', Icons.info_outline),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Outlet ID', outlet.id, Icons.fingerprint),
|
||||
_buildInfoRow(
|
||||
'Organization ID',
|
||||
outlet.organizationId,
|
||||
Icons.business,
|
||||
_buildSectionTitle(
|
||||
context.lang.outlet_information,
|
||||
Icons.info_outline,
|
||||
),
|
||||
SpaceHeight(20),
|
||||
_buildInfoRow(
|
||||
context.lang.address,
|
||||
outlet.address,
|
||||
Icons.location_on,
|
||||
),
|
||||
_buildInfoRow(
|
||||
context.lang.phone_number,
|
||||
outlet.phoneNumber,
|
||||
Icons.phone,
|
||||
),
|
||||
_buildInfoRow('Address', outlet.address, Icons.location_on),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactSection(Outlet outlet) {
|
||||
return _buildAnimatedCard(
|
||||
delay: 400,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Contact Information', Icons.contact_phone),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Phone Number', outlet.phoneNumber, Icons.phone),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBusinessSection(Outlet outlet) {
|
||||
Widget _buildBusinessSection(BuildContext context, Outlet outlet) {
|
||||
return _buildAnimatedCard(
|
||||
delay: 600,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Business Settings', Icons.settings_applications),
|
||||
_buildSectionTitle(
|
||||
context.lang.business_settings,
|
||||
Icons.settings_applications,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Currency', outlet.currency, Icons.monetization_on),
|
||||
_buildInfoRow('Tax Rate', '${outlet.taxRate}%', Icons.percent),
|
||||
_buildInfoRow(
|
||||
context.lang.currency,
|
||||
outlet.currency,
|
||||
Icons.monetization_on,
|
||||
),
|
||||
_buildInfoRow(
|
||||
context.lang.tax_rate,
|
||||
'${outlet.taxRate}%',
|
||||
Icons.percent,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusSection(Outlet outlet) {
|
||||
Widget _buildStatusSection(BuildContext context, Outlet outlet) {
|
||||
return _buildAnimatedCard(
|
||||
delay: 800,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Status', Icons.toggle_on),
|
||||
_buildSectionTitle(context.lang.status_text, Icons.toggle_on),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
@ -272,7 +273,7 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
outlet.isActive ? 'Active' : 'Inactive',
|
||||
outlet.isActive ? context.lang.active : context.lang.inactive,
|
||||
style: AppStyle.md.copyWith(
|
||||
color: outlet.isActive ? AppColor.success : AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -286,29 +287,6 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimestampSection(Outlet outlet) {
|
||||
return _buildAnimatedCard(
|
||||
delay: 1000,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('Timestamps', Icons.schedule),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow(
|
||||
'Created At',
|
||||
_formatDateTime(outlet.createdAt),
|
||||
Icons.add_circle_outline,
|
||||
),
|
||||
_buildInfoRow(
|
||||
'Updated At',
|
||||
_formatDateTime(outlet.updatedAt),
|
||||
Icons.update,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAnimatedCard({required Widget child, required int delay}) {
|
||||
return TweenAnimationBuilder<double>(
|
||||
duration: Duration(milliseconds: 600 + delay),
|
||||
@ -396,8 +374,4 @@ class _OutletInformationPageState extends State<OutletInformationPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDateTime(DateTime dateTime) {
|
||||
return '${dateTime.day}/${dateTime.month}/${dateTime.year} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/analytic/product_analytic_loader/product_analytic_loader_bloc.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
import '../../../../injection.dart';
|
||||
@ -136,18 +137,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: AppColor.primaryGradient,
|
||||
),
|
||||
),
|
||||
child: const CustomAppBar(title: "Analisis Produk"),
|
||||
),
|
||||
),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.product_analytic),
|
||||
);
|
||||
}
|
||||
|
||||
@ -214,7 +204,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
icon: Icons.monetization_on,
|
||||
title: 'Total Pendapatan',
|
||||
title: context.lang.total_revenue,
|
||||
value: _formatCurrency(stats.totalRevenue),
|
||||
color: AppColor.success,
|
||||
),
|
||||
@ -223,7 +213,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
icon: Icons.shopping_cart,
|
||||
title: 'Total Terjual',
|
||||
title: context.lang.total_sold,
|
||||
value: _formatNumber(stats.totalQuantity),
|
||||
color: AppColor.info,
|
||||
),
|
||||
@ -236,7 +226,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
icon: Icons.receipt_long,
|
||||
title: 'Total Pesanan',
|
||||
title: context.lang.total_orders,
|
||||
value: _formatNumber(stats.totalOrders),
|
||||
color: AppColor.warning,
|
||||
),
|
||||
@ -245,7 +235,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
icon: Icons.trending_up,
|
||||
title: 'Rata-rata Nilai',
|
||||
title: context.lang.average_price,
|
||||
value: _formatCurrency(stats.averageOrderValue),
|
||||
color: AppColor.primary,
|
||||
),
|
||||
@ -541,7 +531,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
Icon(Icons.trending_up, color: AppColor.success, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Pendapatan',
|
||||
context.lang.revenue,
|
||||
style: _getTextStyle(
|
||||
AppStyle.sm,
|
||||
color: AppColor.success,
|
||||
@ -552,7 +542,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_formatCurrency(product.revenue.toDouble()),
|
||||
product.revenue.currencyFormatRp,
|
||||
style: _getTextStyle(
|
||||
AppStyle.xl,
|
||||
color: AppColor.success,
|
||||
@ -570,14 +560,14 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
_buildStatCard(
|
||||
icon: Icons.shopping_cart_outlined,
|
||||
value: '${product.quantitySold}',
|
||||
label: 'Terjual',
|
||||
label: context.lang.sold,
|
||||
color: AppColor.info,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildStatCard(
|
||||
icon: Icons.receipt_outlined,
|
||||
value: '${product.orderCount}',
|
||||
label: 'Pesanan',
|
||||
label: context.lang.orders,
|
||||
color: AppColor.warning,
|
||||
),
|
||||
],
|
||||
@ -645,7 +635,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Harga Rata-rata',
|
||||
context.lang.average_price,
|
||||
style: _getTextStyle(
|
||||
AppStyle.sm,
|
||||
color: AppColor.textSecondary,
|
||||
@ -653,7 +643,7 @@ class _ProductAnalyticPageState extends State<ProductAnalyticPage>
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_formatCurrency(product.averagePrice),
|
||||
product.averagePrice.round().currencyFormatRp,
|
||||
style: _getTextStyle(
|
||||
AppStyle.sm,
|
||||
color: _getCategoryColor(product.categoryName),
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:shimmer/shimmer.dart';
|
||||
|
||||
import '../../../../application/category/category_loader/category_loader_bloc.dart';
|
||||
import '../../../../application/product/product_loader/product_loader_bloc.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/category/category.dart';
|
||||
import '../../../../domain/product/product.dart';
|
||||
@ -83,7 +84,7 @@ class _ProductPageState extends State<ProductPage>
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
_buildSliverAppBar(),
|
||||
_buildSliverAppBar(context),
|
||||
_buildCategoryFilter(),
|
||||
_buildProductContent(state),
|
||||
],
|
||||
@ -95,13 +96,13 @@ class _ProductPageState extends State<ProductPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSliverAppBar() {
|
||||
Widget _buildSliverAppBar(BuildContext context) {
|
||||
return SliverAppBar(
|
||||
expandedHeight: 120.0,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
flexibleSpace: CustomAppBar(title: 'Produk'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.product),
|
||||
actions: [
|
||||
ActionIconButton(onTap: () {}, icon: LineIcons.search),
|
||||
ActionIconButton(
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../../application/user/change_password_form/change_password_form_bloc.dart';
|
||||
import '../../../../../common/extension/extension.dart';
|
||||
import '../../../../../common/theme/theme.dart';
|
||||
import '../../../../../injection.dart';
|
||||
import '../../../../components/appbar/appbar.dart';
|
||||
@ -39,7 +40,7 @@ class _ProfileChangePasswordPageState extends State<ProfileChangePasswordPage> {
|
||||
(f) => AppFlushbar.showUserFailureToast(context, f),
|
||||
(user) {
|
||||
if (context.mounted) {
|
||||
AppFlushbar.showSuccess(context, 'Password changed');
|
||||
AppFlushbar.showSuccess(context, context.lang.password_changed);
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
context.router.back();
|
||||
});
|
||||
@ -59,7 +60,7 @@ class _ProfileChangePasswordPageState extends State<ProfileChangePasswordPage> {
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Change Password'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.change_password),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child:
|
||||
@ -83,7 +84,7 @@ class _ProfileChangePasswordPageState extends State<ProfileChangePasswordPage> {
|
||||
ChangePasswordNewPassword(),
|
||||
SpaceHeight(24),
|
||||
AppElevatedButton(
|
||||
text: 'Save',
|
||||
text: context.lang.save,
|
||||
isLoading: state.isSubmitting,
|
||||
onPressed: () {
|
||||
context.read<ChangePasswordFormBloc>().add(
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../../../application/user/change_password_form/change_password_form_bloc.dart';
|
||||
import '../../../../../../common/extension/extension.dart';
|
||||
import '../../../../../components/field/field.dart';
|
||||
|
||||
class ChangePasswordCurrentPassword extends StatelessWidget {
|
||||
@ -11,9 +12,9 @@ class ChangePasswordCurrentPassword extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppPasswordTextFormField(
|
||||
title: 'Current Password',
|
||||
title: context.lang.current_password,
|
||||
prefixIcon: LineIcons.lock,
|
||||
hintText: 'Please enter your current password',
|
||||
hintText: context.lang.current_password_placeholder,
|
||||
onChanged: (value) => context.read<ChangePasswordFormBloc>().add(
|
||||
ChangePasswordFormEvent.currentPasswordChanged(value),
|
||||
),
|
||||
@ -23,7 +24,7 @@ class ChangePasswordCurrentPassword extends StatelessWidget {
|
||||
.state
|
||||
.currentPassword
|
||||
.isEmpty) {
|
||||
return 'Please enter your current password';
|
||||
return context.lang.current_password_placeholder;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../../../application/user/change_password_form/change_password_form_bloc.dart';
|
||||
import '../../../../../../common/extension/extension.dart';
|
||||
import '../../../../../components/field/field.dart';
|
||||
|
||||
class ChangePasswordNewPassword extends StatelessWidget {
|
||||
@ -11,20 +12,20 @@ class ChangePasswordNewPassword extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppPasswordTextFormField(
|
||||
title: 'New Password',
|
||||
title: context.lang.new_password,
|
||||
prefixIcon: LineIcons.lock,
|
||||
hintText: 'Please enter your new password',
|
||||
hintText: context.lang.new_password_placeholder,
|
||||
onChanged: (value) => context.read<ChangePasswordFormBloc>().add(
|
||||
ChangePasswordFormEvent.newPasswordChanged(value),
|
||||
),
|
||||
validator: (value) {
|
||||
if (context.read<ChangePasswordFormBloc>().state.newPassword.isEmpty) {
|
||||
return 'Please enter your new password';
|
||||
return context.lang.new_password_placeholder;
|
||||
}
|
||||
|
||||
if (context.read<ChangePasswordFormBloc>().state.newPassword ==
|
||||
context.read<ChangePasswordFormBloc>().state.currentPassword) {
|
||||
return 'New password cannot be same as current password';
|
||||
return context.lang.new_password_not_same;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../../application/auth/auth_bloc.dart';
|
||||
import '../../../../../application/user/user_edit_form/user_edit_form_bloc.dart';
|
||||
import '../../../../../common/extension/extension.dart';
|
||||
import '../../../../../common/theme/theme.dart';
|
||||
import '../../../../../domain/user/user.dart';
|
||||
import '../../../../../injection.dart';
|
||||
@ -64,7 +65,7 @@ class _ProfileEditPageState extends State<ProfileEditPage> {
|
||||
pinned: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Profile Edit'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.edit_profile),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: BlocBuilder<UserEditFormBloc, UserEditFormState>(
|
||||
@ -85,7 +86,7 @@ class _ProfileEditPageState extends State<ProfileEditPage> {
|
||||
ProfileEditNameField(controller: nameController),
|
||||
SpaceHeight(24),
|
||||
AppElevatedButton(
|
||||
text: 'Save',
|
||||
text: context.lang.save,
|
||||
isLoading: state.isSubmitting,
|
||||
onPressed: () {
|
||||
context.read<UserEditFormBloc>().add(
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../../../application/user/user_edit_form/user_edit_form_bloc.dart';
|
||||
import '../../../../../../common/extension/extension.dart';
|
||||
import '../../../../../components/field/field.dart';
|
||||
|
||||
class ProfileEditNameField extends StatelessWidget {
|
||||
@ -12,9 +13,9 @@ class ProfileEditNameField extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppTextFormField(
|
||||
title: 'Name',
|
||||
title: context.lang.name,
|
||||
prefixIcon: LineIcons.user,
|
||||
hintText: 'Please enter your name',
|
||||
hintText: context.lang.name_placeholder,
|
||||
controller: controller,
|
||||
onChanged: (value) {
|
||||
context.read<UserEditFormBloc>().add(
|
||||
@ -23,7 +24,7 @@ class ProfileEditNameField extends StatelessWidget {
|
||||
},
|
||||
validator: (value) {
|
||||
if (context.read<UserEditFormBloc>().state.name.isEmpty) {
|
||||
return 'Please enter your name';
|
||||
return context.lang.name_placeholder;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:loader_overlay/loader_overlay.dart';
|
||||
|
||||
import '../../../application/auth/auth_bloc.dart';
|
||||
import '../../../application/auth/logout_form/logout_form_bloc.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../components/button/button.dart';
|
||||
@ -89,7 +90,7 @@ class ProfilePage extends StatelessWidget implements AutoRouteWrapper {
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Profile',
|
||||
context.lang.profile,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
|
||||
@ -34,7 +34,7 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Account Information',
|
||||
context.lang.account_information,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -45,7 +45,7 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: LineIcons.envelope,
|
||||
title: 'Email',
|
||||
title: context.lang.email,
|
||||
subtitle: user.email,
|
||||
showArrow: false,
|
||||
),
|
||||
@ -54,7 +54,7 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: LineIcons.calendarAlt,
|
||||
title: 'Member Since',
|
||||
title: context.lang.member_since,
|
||||
subtitle: user.createdAt.toDate,
|
||||
showArrow: false,
|
||||
),
|
||||
@ -62,15 +62,15 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: LineIcons.userEdit,
|
||||
title: 'Ubah Profil',
|
||||
subtitle: 'Ubah profil kamu',
|
||||
title: context.lang.edit_profile,
|
||||
subtitle: context.lang.edit_profile_desc,
|
||||
onTap: () => context.router.push(ProfileEditRoute(user: user)),
|
||||
),
|
||||
ProfileDivider(),
|
||||
ProfileTile(
|
||||
icon: LineIcons.lock,
|
||||
title: 'Change Password',
|
||||
subtitle: 'Change your password',
|
||||
title: context.lang.change_password,
|
||||
subtitle: context.lang.change_password_desc,
|
||||
onTap: () => context.router.push(ProfileChangePasswordRoute()),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'profile_tile.dart';
|
||||
@ -37,7 +38,7 @@ class _ProfileAppSettingState extends State<ProfileAppSetting> {
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'App Settings',
|
||||
context.lang.app_settings,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -74,8 +75,8 @@ class _ProfileAppSettingState extends State<ProfileAppSetting> {
|
||||
// ProfileDivider(),
|
||||
ProfileTile(
|
||||
icon: Icons.language_outlined,
|
||||
title: 'Language',
|
||||
subtitle: 'English (US)',
|
||||
title: context.lang.language,
|
||||
subtitle: context.lang.language_desc,
|
||||
onTap: () => context.router.push(LanguageRoute()),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'divider.dart';
|
||||
@ -30,7 +31,7 @@ class ProfileBusinessSetting extends StatelessWidget {
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Business Settings',
|
||||
context.lang.business_settings,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -41,8 +42,8 @@ class ProfileBusinessSetting extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.business_outlined,
|
||||
title: 'Outlet Information',
|
||||
subtitle: 'Manage your Outlet details',
|
||||
title: context.lang.outlet_information,
|
||||
subtitle: context.lang.outlet_informatio_desc,
|
||||
onTap: () => context.router.push(OutletInformationRoute()),
|
||||
),
|
||||
|
||||
@ -50,8 +51,8 @@ class ProfileBusinessSetting extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.people_outline,
|
||||
title: 'Staff Management',
|
||||
subtitle: 'Manage employees and permissions',
|
||||
title: context.lang.staff_management,
|
||||
subtitle: context.lang.staff_management_desc,
|
||||
onTap: () => context.router.push(ComingSoonRoute()),
|
||||
),
|
||||
|
||||
@ -59,16 +60,16 @@ class ProfileBusinessSetting extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.inventory_2_outlined,
|
||||
title: 'Product',
|
||||
subtitle: 'Manage your products',
|
||||
title: context.lang.products,
|
||||
subtitle: context.lang.manage_your_products,
|
||||
onTap: () => context.router.push(ProductRoute()),
|
||||
),
|
||||
ProfileDivider(),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.download_outlined,
|
||||
title: 'Download Report',
|
||||
subtitle: 'Download your sales or inventory report',
|
||||
title: context.lang.download_report,
|
||||
subtitle: context.lang.download_report_desc,
|
||||
onTap: () => context.router.push(DownloadReportRoute()),
|
||||
),
|
||||
],
|
||||
|
||||
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/auth/logout_form/logout_form_bloc.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
import 'profile_tile.dart';
|
||||
|
||||
class ProfileDangerZone extends StatelessWidget {
|
||||
@ -28,27 +28,10 @@ class ProfileDangerZone extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.warning_outlined, color: AppColor.error, size: 20),
|
||||
const SpaceWidth(8),
|
||||
Text(
|
||||
'Danger Zone',
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.error,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.logout,
|
||||
title: 'Logout',
|
||||
subtitle: 'Sign out from your account',
|
||||
title: context.lang.logout,
|
||||
subtitle: context.lang.logout_desc,
|
||||
iconColor: AppColor.error,
|
||||
textColor: AppColor.error,
|
||||
onTap: () {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../router/app_router.gr.dart';
|
||||
import 'divider.dart';
|
||||
@ -30,7 +31,7 @@ class ProfileSupport extends StatelessWidget {
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Support',
|
||||
context.lang.support,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -41,8 +42,8 @@ class ProfileSupport extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.help_outline,
|
||||
title: 'Help Center',
|
||||
subtitle: 'Get help and support',
|
||||
title: context.lang.help_center,
|
||||
subtitle: context.lang.help_center_desc,
|
||||
onTap: () => context.router.push(ComingSoonRoute()),
|
||||
),
|
||||
|
||||
@ -60,8 +61,8 @@ class ProfileSupport extends StatelessWidget {
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.info_outline,
|
||||
title: 'About',
|
||||
subtitle: 'App version and information',
|
||||
title: context.lang.about,
|
||||
subtitle: context.lang.about_desc,
|
||||
onTap: () => context.router.push(AboutAppRoute()),
|
||||
),
|
||||
],
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
import 'widgets/purchase_tile.dart';
|
||||
@ -93,7 +94,7 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
elevation: 0,
|
||||
backgroundColor: AppColor.primary,
|
||||
|
||||
flexibleSpace: CustomAppBar(title: 'Pembelian'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.purchase),
|
||||
),
|
||||
|
||||
// Stats Cards
|
||||
@ -105,7 +106,7 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
children: [
|
||||
Expanded(
|
||||
child: PurchaseStatCard(
|
||||
title: 'Total Pembelian',
|
||||
title: context.lang.total_purchase,
|
||||
value: 'Rp 8.340.000',
|
||||
icon: LineIcons.shoppingCart,
|
||||
iconColor: AppColor.success,
|
||||
@ -115,8 +116,8 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: PurchaseStatCard(
|
||||
title: 'Pending Order',
|
||||
value: '3 Orders',
|
||||
title: context.lang.pending_order,
|
||||
value: '3 ${context.lang.orders}',
|
||||
icon: LineIcons.clock,
|
||||
iconColor: AppColor.warning,
|
||||
cardAnimation: cardAnimation,
|
||||
@ -139,7 +140,7 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Riwayat Pembelian',
|
||||
context.lang.history_purchase,
|
||||
style: AppStyle.lg.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
@ -154,7 +155,7 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
'${purchaseData.length} Orders',
|
||||
'${purchaseData.length} ${context.lang.orders}',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
),
|
||||
@ -220,20 +221,6 @@ class _PurchasePageState extends State<PurchasePage>
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 80)),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
// Navigate to create new purchase
|
||||
},
|
||||
backgroundColor: AppColor.secondary,
|
||||
icon: const Icon(LineIcons.plus, color: AppColor.textWhite),
|
||||
label: Text(
|
||||
'Buat Pembelian',
|
||||
style: AppStyle.md.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
|
||||
class PurchaseTile extends StatelessWidget {
|
||||
@ -219,7 +220,7 @@ class PurchaseTile extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${purchase['items']} items',
|
||||
'${purchase['items']} ${context.lang.items}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.secondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -241,7 +242,7 @@ class PurchaseTile extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Total Pembelian',
|
||||
context.lang.total_purchase,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import '../../../application/analytic/dashboard_analytic_loader/dashboard_analytic_loader_bloc.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
import '../../components/button/button.dart';
|
||||
import '../../components/field/date_range_picker_field.dart';
|
||||
import '../../components/spacer/spacer.dart';
|
||||
import 'widgets/payment_method.dart';
|
||||
@ -117,17 +116,17 @@ class _ReportPageState extends State<ReportPage> with TickerProviderStateMixin {
|
||||
backgroundColor: AppColor.primary,
|
||||
centerTitle: false,
|
||||
flexibleSpace: CustomAppBar(
|
||||
title: 'Laporan',
|
||||
title: context.lang.report,
|
||||
isBack: false,
|
||||
),
|
||||
actions: [
|
||||
ActionIconButton(
|
||||
onTap: () {},
|
||||
icon: LineIcons.download,
|
||||
),
|
||||
ActionIconButton(onTap: () {}, icon: LineIcons.filter),
|
||||
SpaceWidth(8),
|
||||
],
|
||||
// actions: [
|
||||
// ActionIconButton(
|
||||
// onTap: () {},
|
||||
// icon: LineIcons.download,
|
||||
// ),
|
||||
// ActionIconButton(onTap: () {}, icon: LineIcons.filter),
|
||||
// SpaceWidth(8),
|
||||
// ],
|
||||
),
|
||||
|
||||
SliverToBoxAdapter(
|
||||
|
||||
@ -57,7 +57,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Payment Methods',
|
||||
context.lang.payment_methods,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
@ -65,7 +65,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Revenue breakdown by payment method',
|
||||
context.lang.payment_methods_desc,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
@ -80,7 +80,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
|
||||
// Payment Method List
|
||||
if (paymentMethods.isEmpty)
|
||||
_buildEmptyState()
|
||||
_buildEmptyState(context)
|
||||
else
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
@ -190,12 +190,12 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Stats row with better spacing
|
||||
_buildStatsSection(method),
|
||||
_buildStatsSection(context, method),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Enhanced progress bar section
|
||||
_buildProgressSection(method, index),
|
||||
_buildProgressSection(context, method, index),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -410,7 +410,10 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatsSection(DashboardPaymentMethod method) {
|
||||
Widget _buildStatsSection(
|
||||
BuildContext context,
|
||||
DashboardPaymentMethod method,
|
||||
) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
@ -426,7 +429,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Total Revenue',
|
||||
context.lang.total_revenue,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -457,7 +460,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'Orders',
|
||||
context.lang.orders,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -480,7 +483,11 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProgressSection(DashboardPaymentMethod method, int index) {
|
||||
Widget _buildProgressSection(
|
||||
BuildContext context,
|
||||
DashboardPaymentMethod method,
|
||||
int index,
|
||||
) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -488,7 +495,7 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Revenue Share',
|
||||
context.lang.revenue_share,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -560,11 +567,10 @@ class ReportPaymentMethod extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
return EmptyWidget(
|
||||
title: 'No Payment Methods',
|
||||
message:
|
||||
'Payment method data will appear here once transactions are made',
|
||||
title: context.lang.no_payment_methods,
|
||||
message: context.lang.no_payment_methods_desc,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ class ReportQuickStats extends StatelessWidget {
|
||||
return Transform.scale(
|
||||
scale: value,
|
||||
child: ReportStatTile(
|
||||
title: 'Total Orders',
|
||||
title: context.lang.total_orders,
|
||||
value: overview.totalOrders.toString(),
|
||||
icon: Icons.receipt_long,
|
||||
color: AppColor.info,
|
||||
@ -43,7 +43,7 @@ class ReportQuickStats extends StatelessWidget {
|
||||
return Transform.scale(
|
||||
scale: value,
|
||||
child: ReportStatTile(
|
||||
title: 'Average Order',
|
||||
title: context.lang.average_price,
|
||||
value: overview.averageOrderValue
|
||||
.round()
|
||||
.currencyFormatRp,
|
||||
@ -70,7 +70,7 @@ class ReportQuickStats extends StatelessWidget {
|
||||
return Transform.scale(
|
||||
scale: value,
|
||||
child: ReportStatTile(
|
||||
title: 'Customers',
|
||||
title: context.lang.customer,
|
||||
value: overview.totalCustomers.toString(),
|
||||
icon: Icons.people,
|
||||
color: AppColor.success,
|
||||
@ -89,7 +89,8 @@ class ReportQuickStats extends StatelessWidget {
|
||||
return Transform.scale(
|
||||
scale: value,
|
||||
child: ReportStatTile(
|
||||
title: 'Void + Refund',
|
||||
title:
|
||||
'${context.lang.void_text} + ${context.lang.refund}',
|
||||
value: (overview.voidedOrders + overview.refundedOrders)
|
||||
.toString(),
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ class ReportRevenueSummary extends StatelessWidget {
|
||||
),
|
||||
const SpaceWidth(12),
|
||||
Text(
|
||||
'Total Pendapatan',
|
||||
context.lang.total_revenue,
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@ -37,7 +37,7 @@ class ReportSales extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Sales Chart',
|
||||
context.lang.sales_chart,
|
||||
style: AppStyle.xxl.copyWith(
|
||||
color: AppColor.textPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -46,8 +46,8 @@ class ReportSales extends StatelessWidget {
|
||||
const SpaceHeight(4),
|
||||
Text(
|
||||
salesData.isEmpty
|
||||
? 'No data available'
|
||||
: '${salesData.length} days overview',
|
||||
? context.lang.no_data_available
|
||||
: context.lang.total_days_overview(salesData.length),
|
||||
style: AppStyle.md.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
],
|
||||
@ -71,13 +71,13 @@ class ReportSales extends StatelessWidget {
|
||||
|
||||
// Sales Summary Cards
|
||||
if (salesData.isNotEmpty) ...[
|
||||
_buildSalesSummary(),
|
||||
_buildSalesSummary(context),
|
||||
const SpaceHeight(20),
|
||||
],
|
||||
|
||||
// Chart Container
|
||||
salesData.isEmpty
|
||||
? _buildEmptyChart()
|
||||
? _buildEmptyChart(context)
|
||||
: Container(
|
||||
height: 300,
|
||||
padding: const EdgeInsets.all(16),
|
||||
@ -105,14 +105,16 @@ class ReportSales extends StatelessWidget {
|
||||
if (salesData.isNotEmpty)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [_buildLegendItem('Sales Data', AppColor.primary)],
|
||||
children: [
|
||||
_buildLegendItem(context.lang.sales_data, AppColor.primary),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSalesSummary() {
|
||||
Widget _buildSalesSummary(BuildContext context) {
|
||||
final totalSales = salesData.fold<int>(0, (sum, item) => sum + item.sales);
|
||||
final totalOrders = salesData.fold<int>(
|
||||
0,
|
||||
@ -137,7 +139,7 @@ class ReportSales extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
'Total Sales',
|
||||
context.lang.total_sales,
|
||||
totalSales.currencyFormatRp,
|
||||
Icons.attach_money,
|
||||
AppColor.success,
|
||||
@ -151,7 +153,7 @@ class ReportSales extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
'Net Sales',
|
||||
context.lang.net_sales,
|
||||
totalNetSales.currencyFormatRp,
|
||||
Icons.trending_up,
|
||||
AppColor.primary,
|
||||
@ -164,7 +166,7 @@ class ReportSales extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
'Total Orders',
|
||||
context.lang.total_orders,
|
||||
totalOrders.toString(),
|
||||
Icons.shopping_cart,
|
||||
AppColor.info,
|
||||
@ -178,7 +180,7 @@ class ReportSales extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
'Total Items',
|
||||
context.lang.total_items,
|
||||
totalItems.toString(),
|
||||
Icons.inventory,
|
||||
AppColor.warning,
|
||||
@ -204,11 +206,15 @@ class ReportSales extends StatelessWidget {
|
||||
children: [
|
||||
Icon(icon, size: 16, color: color),
|
||||
const SpaceWidth(6),
|
||||
Text(
|
||||
label,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -395,10 +401,10 @@ class ReportSales extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyChart() {
|
||||
Widget _buildEmptyChart(BuildContext context) {
|
||||
return EmptyWidget(
|
||||
title: 'No Sales Data',
|
||||
message: 'Sales data will appear here once transactions are recorded',
|
||||
title: context.lang.no_sales_data,
|
||||
message: context.lang.no_sales_data_desc,
|
||||
emptyIcon: Icons.show_chart,
|
||||
);
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Produk Terlaris',
|
||||
context.lang.best_selling_products,
|
||||
style: AppStyle.xxl.copyWith(
|
||||
color: AppColor.textPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -42,7 +42,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
),
|
||||
const SpaceHeight(4),
|
||||
Text(
|
||||
'Ranking penjualan tertinggi',
|
||||
context.lang.highest_sales_ranking,
|
||||
style: AppStyle.md.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
],
|
||||
@ -66,7 +66,11 @@ class ReportTopProduct extends StatelessWidget {
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildEnhancedProductItem(products[index], index + 1);
|
||||
return _buildEnhancedProductItem(
|
||||
context,
|
||||
products[index],
|
||||
index + 1,
|
||||
);
|
||||
},
|
||||
itemCount: products.length,
|
||||
),
|
||||
@ -75,7 +79,11 @@ class ReportTopProduct extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEnhancedProductItem(DashboardTopProduct product, int rank) {
|
||||
Widget _buildEnhancedProductItem(
|
||||
BuildContext context,
|
||||
DashboardTopProduct product,
|
||||
int rank,
|
||||
) {
|
||||
final isFirst = rank == 1;
|
||||
final isTopThree = rank <= 3;
|
||||
|
||||
@ -320,7 +328,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
const SpaceWidth(3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Revenue',
|
||||
context.lang.revenue,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -364,7 +372,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Avg. Price',
|
||||
context.lang.average_price,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -423,7 +431,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
const SpaceWidth(3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Sold',
|
||||
context.lang.sold,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -465,7 +473,7 @@ class ReportTopProduct extends StatelessWidget {
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Orders',
|
||||
context.lang.orders,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -543,7 +551,9 @@ class ReportTopProduct extends StatelessWidget {
|
||||
const SpaceWidth(5),
|
||||
Flexible(
|
||||
child: Text(
|
||||
isFirst ? 'Best Seller' : 'Top Performer',
|
||||
isFirst
|
||||
? context.lang.best_seller
|
||||
: context.lang.top_performer,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: isFirst
|
||||
? AppColor.warning
|
||||
|
||||
@ -97,7 +97,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Penjualan'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.sales),
|
||||
),
|
||||
|
||||
// Date Range Header
|
||||
@ -138,7 +138,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Summary',
|
||||
context.lang.summary,
|
||||
style: AppStyle.xxl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
@ -177,7 +177,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||
child: Text(
|
||||
'Daily Breakdown',
|
||||
context.lang.daily_breakdown,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -412,7 +412,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
'Total Sales',
|
||||
context.lang.total_sales,
|
||||
state.sales.summary.totalSales.currencyFormatRp,
|
||||
Icons.trending_up,
|
||||
AppColor.success,
|
||||
@ -422,7 +422,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
SpaceWidth(12),
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
'Total Orders',
|
||||
context.lang.total_orders,
|
||||
state.sales.summary.totalOrders.toString(),
|
||||
Icons.shopping_cart,
|
||||
AppColor.info,
|
||||
@ -446,7 +446,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
'Avg Order Value',
|
||||
context.lang.average_price,
|
||||
state.sales.summary.averageOrderValue
|
||||
.round()
|
||||
.currencyFormatRp,
|
||||
@ -458,7 +458,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
SpaceWidth(12),
|
||||
Expanded(
|
||||
child: _buildSummaryCard(
|
||||
'Total Items',
|
||||
context.lang.total_items,
|
||||
state.sales.summary.totalItems.toString(),
|
||||
Icons.inventory,
|
||||
AppColor.primary,
|
||||
@ -530,7 +530,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Net Sales',
|
||||
context.lang.net_sales,
|
||||
style: TextStyle(
|
||||
color: AppColor.textWhite.withOpacity(0.9),
|
||||
fontSize: 14,
|
||||
@ -668,7 +668,7 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
'${dailySale.orders} orders',
|
||||
'${dailySale.orders} ${context.lang.orders}',
|
||||
style: TextStyle(
|
||||
color: AppColor.info,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -683,21 +683,21 @@ class _SalesPageState extends State<SalesPage> with TickerProviderStateMixin {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildDetailItem(
|
||||
'Items',
|
||||
context.lang.items,
|
||||
'${dailySale.items}',
|
||||
Icons.inventory_2,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildDetailItem(
|
||||
'Tax',
|
||||
context.lang.tax,
|
||||
dailySale.tax.currencyFormatRp,
|
||||
Icons.receipt,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildDetailItem(
|
||||
'Discount',
|
||||
context.lang.discount,
|
||||
dailySale.discount.currencyFormatRp,
|
||||
Icons.local_offer,
|
||||
),
|
||||
|
||||
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
|
||||
@ -213,7 +214,7 @@ class _SchedulePageState extends State<SchedulePage>
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
flexibleSpace: CustomAppBar(title: 'Jadwal'),
|
||||
flexibleSpace: CustomAppBar(title: context.lang.schedule),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user