add structure
11
.vscode/launch.json
vendored
@@ -2,9 +2,14 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Flutter",
|
||||
"name": "Flutter Dev",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
}
|
||||
"type": "dart",
|
||||
"program": "lib/main-dev.dart",
|
||||
"args": [
|
||||
"-t",
|
||||
"lib/main-dev.dart",
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -34,7 +34,7 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.mokkon.fcs_dev.fcs"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
BIN
assets/MyMMUnicodeUniversal.ttf
Normal file
BIN
assets/address.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
assets/admin.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/amount.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/approve.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/block.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/buyer.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/date_filter.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/device.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/do.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/email.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
assets/employee.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
assets/eng_flag.png
Executable file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/fonts/MyMMUnicodeUniversal.ttf
Normal file
BIN
assets/fonts/OpenSans-Regular.ttf
Normal file
BIN
assets/fonts/Roboto-Regular.ttf
Normal file
BIN
assets/fonts/Work_Sans/WorkSans-Black.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-Bold.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-ExtraBold.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-ExtraLight.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-Light.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-Medium.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-Regular.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-SemiBold.ttf
Executable file
BIN
assets/fonts/Work_Sans/WorkSans-Thin.ttf
Executable file
BIN
assets/gender.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/img/bg.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/img/login_logo.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/inventory.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
assets/licence.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
467
assets/local/localization_en.json
Normal file
@@ -0,0 +1,467 @@
|
||||
{
|
||||
"btn.save": "Save",
|
||||
"btn.approve":"Approve",
|
||||
|
||||
"product": "Product",
|
||||
"price": "Price",
|
||||
"volume": "Quantity",
|
||||
"amount": "Amount",
|
||||
"storage" :"Storage",
|
||||
|
||||
"welcome.price.updateinfo":"Last updated on :{0}",
|
||||
"welcome.price.trend":"Price trend",
|
||||
"welcome.price.detail":"Details",
|
||||
|
||||
"products.title":"Products",
|
||||
"prodcuts":"Products",
|
||||
"prices":"Prices",
|
||||
"product.confirm":"Confirm to update product price?",
|
||||
"products.prices":"Prices",
|
||||
"products.gas":"Products",
|
||||
"product.item":"Product",
|
||||
"product.name":"Product Name",
|
||||
"product.name_empty":"Please enter product name",
|
||||
"product.new_price":"New Price",
|
||||
"product.price_empty":"Please enter price",
|
||||
"product.order":"Display Order",
|
||||
"product.order_empty":"Please enter display order",
|
||||
"product.balance_qty":"PO Balance Quantity by Product",
|
||||
"product.qtys":"Balance Quantities",
|
||||
"product.update.date":"Date",
|
||||
"product.close": "Order is closed now.",
|
||||
"product.open": "Order is opened at {0} to {1}\nOn {2}.",
|
||||
"product.purchase.order": "Order",
|
||||
|
||||
"login": "Login",
|
||||
"login.title":"Login",
|
||||
"login.btn":"Login",
|
||||
"login.name":"Name",
|
||||
"login.phone":"Phone Number",
|
||||
"login.password":"Password",
|
||||
"login.confirm_password":"Confirm Password",
|
||||
"login.name_empty": "Please enter your name",
|
||||
"login.phone_empty": "Please enter your phone number",
|
||||
"login.phone_code": "Only starting '+959'",
|
||||
"login.password_empty": "Please enter password",
|
||||
"login.password_size": "At least 6 characters",
|
||||
"login.password_mismatch": "Confirm password mismatch",
|
||||
"login.confirm_password_empty": "Please enter confirm password",
|
||||
"login.sms_empty":"Please enter sms code",
|
||||
"login.sms_size": "Must be 6 characters",
|
||||
"login.forgot_password":"Forgot Password?",
|
||||
"login.email.title":"Recovery Email",
|
||||
"login.email":"Email",
|
||||
"login.email.empty":"Please Enter Email",
|
||||
"login.email.add":"Add",
|
||||
"login.email.skip":"Skip",
|
||||
|
||||
"sing.up":"SIGN UP",
|
||||
"sing.title":"Signup",
|
||||
|
||||
"change.password.title":"Change Password",
|
||||
"change":"CHANGE",
|
||||
"change.phone":"Change Phone Number",
|
||||
"change.new.phone":"New Phone Number",
|
||||
"change.phone_empty": "Please enter new phone number",
|
||||
"change.email":"Recovery Email",
|
||||
"change.pin.title":"Add PIN Code",
|
||||
"change.pin":"PIN Code",
|
||||
"change.pin_empty":"Please enter PIN Code",
|
||||
"change.pin_size":"Must be 6 characters",
|
||||
"pin.add_btn":"UPDATE PIN",
|
||||
"pin.clear_btn":"CLEAR PIN ",
|
||||
"pin.switch":"Clear PIN",
|
||||
|
||||
"forget.password":"Forgot Password",
|
||||
"forget.email":"Email (or) Phone Number",
|
||||
"forget.email.empty":"Please Enter Email (or) Phone Number",
|
||||
"forget.enter":"Enter",
|
||||
|
||||
"reset.password.title":"Reset Password",
|
||||
"reset.sms":"SMS Code",
|
||||
"reset.new_password":"New Password",
|
||||
"reset":"RESET",
|
||||
|
||||
"reg.title": "My Registration",
|
||||
"reg.biz_name": "Business Name",
|
||||
"reg.biz_address": "Business Address",
|
||||
"reg.biz_shops": "Number of Shops",
|
||||
"reg.biz_shop_addresses":"Shop Addresses",
|
||||
"reg.type_shop": "Shop",
|
||||
"reg.type_agent": "Agent",
|
||||
"reg.status": "Status",
|
||||
"reg.table_product": "Product",
|
||||
"reg.table_storage_vol": "Storage Quantity",
|
||||
"reg.table_sale_vol": "Daily Sale Quantity",
|
||||
"reg.quota": "Daily Quota Quantity",
|
||||
"reg.max_quota": "Max Quota Quantity",
|
||||
"reg.quota.used": "Daily Quota Used Quantity",
|
||||
"reg.max_quota.used": "Max Quota Used Quantity",
|
||||
"reg.empty_biz_name": "Empty businuee name",
|
||||
"reg.empty_biz_address":"Empty business address",
|
||||
"reg.empty_shops":"Empty shops",
|
||||
"reg_info.title": "My Registration Info",
|
||||
"reg_info.nric_front": "NRIC Front",
|
||||
"reg_info.nric_back": "NRIC Back",
|
||||
"reg.confirm":"Submit Registration?",
|
||||
"reg.date":"Registeration Date",
|
||||
|
||||
"profile.title": "My Profile",
|
||||
"profile.edit_title": "Edit My Profile",
|
||||
"profile.name": "Name",
|
||||
"profile.phone": "Phone",
|
||||
"profile.language": "Languages",
|
||||
"profile.logout": "logout",
|
||||
"profile.logout.confirm":"Are you sure want to logout?",
|
||||
"profile.devices":"Devices",
|
||||
"profile.email":"Email",
|
||||
"profile.privilege":"Privilege",
|
||||
|
||||
"device.confirm":"Confirm this device?",
|
||||
"device.logout":"Logout this device?",
|
||||
"device.set_primary":"Set this primary device?",
|
||||
|
||||
"po.title":"POs",
|
||||
"po":"PO",
|
||||
"po.approval":"PO Approval",
|
||||
"po.date": "PO Date",
|
||||
"po.number": "PO No.",
|
||||
"po.name":"Buyer Name",
|
||||
"po.po_num": "PO Number",
|
||||
"po.biz":"Business name",
|
||||
"po.status": "PO Status",
|
||||
"po.receipt":"Receipts",
|
||||
"po.po_payment":"PO Payment",
|
||||
"po.po_payment_receipt":"Payment Receipt",
|
||||
"po.storage_receipt":"Storage Receipt",
|
||||
"po.no.photo": "No selected photo",
|
||||
"po.storage_charge" :"Storage Charge",
|
||||
"po.product": "Product",
|
||||
"po.price": "Price",
|
||||
"po.volume": "Volume",
|
||||
"po.balance.volume":"Balance Volume",
|
||||
"po.amount": "Amount",
|
||||
"po.form.volume":"Please enter volume",
|
||||
"po.comment":"Comment",
|
||||
"po.confirm":"Submit Purchase Order?",
|
||||
"po.approve.confirm":"Approve this PO?",
|
||||
"po.cancel.confirm":"Cancel this PO?",
|
||||
"po.counts":"PO Counts",
|
||||
"po.details":"Details",
|
||||
"po.count":"PO Count",
|
||||
"po.balances":"PO Balance Quantity",
|
||||
"po.info":"PO Info",
|
||||
"po.retrieved.amount":"DO Balance\nQty",
|
||||
"po.count.status":"PO Status",
|
||||
"po.avail.qty":"PO Balance\nQty",
|
||||
"po.total_count":"Total Count",
|
||||
|
||||
"do.title":"DOs",
|
||||
"do":"DO",
|
||||
"do.do_date":"DO Date",
|
||||
"do.date": "Delivery Date",
|
||||
"do.licence": "Driver Licence",
|
||||
"do.driver":"Driver Name",
|
||||
"do.car":"Car No.",
|
||||
"do.status": "DO Status",
|
||||
"do.delivery.status": "Delivery Status",
|
||||
"do.delivery.init.time":"Delivery Initiated At",
|
||||
"do.do_num": "DO Number",
|
||||
"do.type": "DO Type",
|
||||
"do.name":"Buyer Name",
|
||||
"do.biz":"Business name",
|
||||
"do.form.date":"Please enter DO date",
|
||||
"do.form.licence":"Please enter car licence",
|
||||
"do.form.driver":"Please enter driver name",
|
||||
"do.form.car":"Please enter car number",
|
||||
"do.product": "Product",
|
||||
"do.price": "Price",
|
||||
"do.volume": "Volume",
|
||||
"do.amount": "Amount",
|
||||
"do.storage":"Storage",
|
||||
"do.quantity":"Quantity",
|
||||
"do.form.volume":"Please enter volume",
|
||||
"do.form.amount":"Please enter amount",
|
||||
"do.po_qty":"PO Qty",
|
||||
"do.po_balance_qty":"PO Balance\nQty",
|
||||
"do.do_qty":"DO Qty",
|
||||
"do.po_num":"PO Number",
|
||||
"do.driver.image":"Driver Image",
|
||||
"do.driver.sign":"Driver Signature",
|
||||
"do.receipt":"Delivery Receipt",
|
||||
"do.no.photo":"No selected photo",
|
||||
"do.storage_charge" :"Storage Charge",
|
||||
"do.storage_receipt":"Storage Receipt",
|
||||
"do.comment":"Comment",
|
||||
"do.confirm":"Submit Delivery Order?",
|
||||
"do.approve.confirm":"Approve this DO?",
|
||||
"do.cancel.confirm":"Cancel this DO?",
|
||||
"do.end.confirm":"End delivery?",
|
||||
"do.receipt.title":"Delivery Receipt",
|
||||
"do.single":"Single",
|
||||
"do.multiple": "Multiple",
|
||||
"do_qty":"DO Qty",
|
||||
"do.product.title":"DO Product",
|
||||
"do.cancel":"Cancel",
|
||||
"do.enter":"Enter",
|
||||
"do.qtys":"DO Qtys",
|
||||
"do.details":"Details",
|
||||
"do.counts":"DO Counts",
|
||||
"do.count":"DO Count",
|
||||
"do.products":"DO Products",
|
||||
"do.count.status":"DO Status",
|
||||
"do.total_count":"Total Count",
|
||||
|
||||
"storage.name":"Storage Name",
|
||||
"storage.form.name":"Please enter storage name",
|
||||
"storage.date":"Date",
|
||||
"storage.form.date":"Please enter date",
|
||||
"storge": "Storage",
|
||||
"storage.product.qty":"Quantity",
|
||||
"storage.delete_confirm":"Delete storage?",
|
||||
|
||||
"name": "Name",
|
||||
"form.name":"Please enter name",
|
||||
|
||||
|
||||
"input_sms": "Input SMS Code",
|
||||
"sms.sms": "Enter SMS Code",
|
||||
"sms.empty":"Please Enter SMS Code",
|
||||
"sms.enter":"Enter",
|
||||
"singin": "Sign In",
|
||||
|
||||
"email.input": "Input Email Code",
|
||||
"email.code":"Enter Email Code",
|
||||
"email.code_empty":"Please Enter SMS Code",
|
||||
|
||||
|
||||
"inventory.takings":"Inventory Takings",
|
||||
"inventory.take":"Take Inventory",
|
||||
"inventory.item":"Inventory",
|
||||
"inventory.product": "Product",
|
||||
"inventory.storage": "Storage",
|
||||
"inventory.quantity": "Quantity",
|
||||
"inventory.old.qty": "Old Quantity",
|
||||
"inventory.new.qty": "New Quantity",
|
||||
"inventory.form.qty":"Please enter quantity",
|
||||
"inventory.confirm":"Submit Inventory Taking?",
|
||||
|
||||
|
||||
"pd":"PD",
|
||||
"pd.product.title":"PD Product",
|
||||
"pd.date" :"Date",
|
||||
"pd.product": "Product",
|
||||
"pd.storage": "Storage",
|
||||
"pd.quantity": "Quantity",
|
||||
"pd.form.quan":"Please enter quantity",
|
||||
"pd.confirm":"Submit Purchase Delivery?",
|
||||
|
||||
"buyer.title":"Buyers",
|
||||
"buyer.type_biz":"Business Type",
|
||||
"buyer.account_name":"Account Name",
|
||||
"buyer.phone_number":"Phone Number",
|
||||
"buyer.quota":"Daily Quota",
|
||||
"buyer.max.quota":"Maximum Quota",
|
||||
"buyer.name":"Buyer",
|
||||
"buyer.product":"Product",
|
||||
"buyer.balQty":"Balance Volume",
|
||||
"buyer.approve.confirm":"Confirm to approve buyer?",
|
||||
"buyer.delete.confirm":"Delete buyer request?",
|
||||
"buyer.allocate.quota.confirm":"Allocate Quota?",
|
||||
|
||||
"term.agree_btn":"Agree",
|
||||
"term.iagree":"I agree on terms and condition.",
|
||||
|
||||
"noti.title":"Notifications",
|
||||
"log.title":"Logs",
|
||||
|
||||
"document.log.title":"Document Logs",
|
||||
"document.date":"Date",
|
||||
"document.by":"By",
|
||||
"document.desc":"Description",
|
||||
|
||||
"contact.title":"Contacts",
|
||||
"contact.phone.title":"Input Phone Number",
|
||||
"contact.phone":"Phone Number",
|
||||
"contact.phone.confim":"Call {0}?",
|
||||
"contact.phone.empty":"Please enter phone number",
|
||||
"contact.email":"Email",
|
||||
"contact.email.configm":"Email to '{0}'?",
|
||||
"contact.email.empty":"Please enter email",
|
||||
"contact.facebook":"Facebook",
|
||||
"contact.facebook.empty":"Please enter facebook url",
|
||||
"contact.google":"Website",
|
||||
"contact.google.empty":"Please enter website url",
|
||||
"contact.confrim":"Confirm update contact?",
|
||||
"contact.open.confrim":"Open '{0}'?",
|
||||
"contact.bank.accounts":"Bank Accounts Info",
|
||||
"contact.address":"Address",
|
||||
"contact.delivery.phone":"Delivery Phone",
|
||||
|
||||
"term.title":"Terms",
|
||||
"manual.title":"Manual",
|
||||
"myreg.title":"My\nRegistration",
|
||||
"storage.title":"Storages",
|
||||
"storage.item.title":"Storage",
|
||||
"pd.title":"PDs",
|
||||
|
||||
"employee.title":"Employees",
|
||||
"employee.item.title":"Employee",
|
||||
"employee.phone":"Phone Number",
|
||||
"employee.add":"Add",
|
||||
"employee.save":"Save",
|
||||
|
||||
"setting.title":"Settings",
|
||||
"setting.confirm":"Confirm update setting?",
|
||||
|
||||
"users.title":"Users",
|
||||
"user.title":"User",
|
||||
"user.save":"Save",
|
||||
"user.block.confirm":"Confirm to block this user?",
|
||||
"user.block_list":"Block lists",
|
||||
"user.unblock.confirm":"Confirm to unblock this user?",
|
||||
|
||||
"banks.title":"Bank Accounts",
|
||||
"banks.edit.title":"Bank Account",
|
||||
"banks.name":"Bank Name",
|
||||
"banks.account.name":"Account Name",
|
||||
"banks.account.number":"Account Number",
|
||||
"banks.account.delete.confirmation":"Delete Bank Account?",
|
||||
|
||||
|
||||
"delivery.title":"Deliveries",
|
||||
"delivery":"Delivery",
|
||||
"delivery.confirm":"End delivery?",
|
||||
"delivery.detail":"Details",
|
||||
"delivery.date":"Date",
|
||||
"delivery.qty":"Quantity",
|
||||
"delivery.do.title":"Delivery DO",
|
||||
"delivery.do.details":"Details",
|
||||
"delivery.do.counts":"Delivery DO Counts",
|
||||
"delivery.do.count":"Delivery DO Count",
|
||||
"delivery.do.summary":"Delivery DO Summary ",
|
||||
"delivery.do.sum.counts":"Delivery DO Summary Counts",
|
||||
"delivery.days":"Days",
|
||||
"delivery.summary":"Delivery Summary",
|
||||
"delivery.sum.amounts":"Delivery Summary Amounts",
|
||||
|
||||
"delivery.data.title":"Deliveries",
|
||||
|
||||
"delivery.detail.title":"{0} Deliveries",
|
||||
|
||||
"chart.daily.title":"Daily Quota ({0} Liters)",
|
||||
"chart.max.title": "Maximum Quota ({0} Liters)",
|
||||
"chart.remaining": "remaining",
|
||||
"chart.used": "used",
|
||||
"chart.revenue":"Revenue",
|
||||
"chart.spending":"Spending",
|
||||
"chart.date":"Date",
|
||||
"chart.30_days":"(Last 30 days)",
|
||||
|
||||
"revenue.amounts":"Revenue Amounts",
|
||||
"revenue.date":"Date",
|
||||
"revenue.amount":"Amount",
|
||||
"revenue.detail":"Details",
|
||||
"revenue.detail.title":"{0} Revenue Amounts",
|
||||
|
||||
"spending.amounts":"Spending Amounts",
|
||||
"spending.detail.title":"{0} Spending Amounts",
|
||||
|
||||
"load": "Loading...",
|
||||
|
||||
"Cancel": "Cancel",
|
||||
"Ok": "Ok",
|
||||
|
||||
"singup": "Sing Up",
|
||||
"buyer.reg": "Buyer Registeration",
|
||||
"po.sub": "PO Submission",
|
||||
"do.sub": "DO Submission",
|
||||
"inventory.taking": "Inventory Taking",
|
||||
"do.approved": "DO approved",
|
||||
"po.approved": "PO approved",
|
||||
"buyer.approved": "Buyer approved",
|
||||
"purchase.delivery": "Purchase Delivery",
|
||||
"do.delivery": "DO Delivery",
|
||||
"product.price": "Product Price",
|
||||
|
||||
"Click 'Initiate Delivery' button": "Click 'Initiate Delivery' button",
|
||||
"Click 'Start Delivery'": "Click 'Start Delivery'",
|
||||
|
||||
"1":"1",
|
||||
"2":"2",
|
||||
"3":"3",
|
||||
|
||||
"signup.slide.initial":"1.Click Arrow button",
|
||||
"signup.slide.s1": "1.Select 'Signup' button \n2.Enter all data info\n3.Click 'SIGNUP' button",
|
||||
"signup.slide.s2" : "1.Enter SMS code\n2.Click 'Sign In' button",
|
||||
"signup.slide.s3": "1.Click 'Agree' button",
|
||||
"signup.slide.s4": "1.Click 'Ok' button",
|
||||
"signup.slide.s5": "Finish SignUp",
|
||||
|
||||
"login.slide.s1":"1.Select 'Login' button\n2.Enter Phone Number and Password\n3.Click 'LOGIN' button",
|
||||
|
||||
"buyerreg.slide.s1":"1.Click 'My Registration' button",
|
||||
"buyerreg.slide.s2":"1.Enter all data info\n2.Click Sent button",
|
||||
"buyerreg.slide.s4":"Finished Registration",
|
||||
|
||||
"posub.slide.s1": "1.Click 'POs'",
|
||||
"posub.slide.s2": "1.Click '+' button",
|
||||
"posub.slide.s3":"1.Click '+' button\n2.Click Sent button",
|
||||
"posub.slide.s4":"1.Enter Volume and select Product\n2.Click 'Save' button",
|
||||
"posub.slide.s6": "Finished PO Submission",
|
||||
|
||||
"dosub.slide.s1": "1.Click 'More' button",
|
||||
"dosub.slide.s2": "Click 'Create DO'",
|
||||
"dosub.slide.s4" : "1.Enter all data info\n2.Click Sent button",
|
||||
"dosub.slide.s6": "Finished DO Submission",
|
||||
|
||||
"storage.slide.s1": "Click Storages",
|
||||
"storage.slide.s3" : "1.Enter storage name\n2.Click 'Save' button",
|
||||
|
||||
"inventory.taking.s1": "Click Inventory Takings",
|
||||
"inventory.taking.s4":"1.Select storage name , product and enter quantity\n2.Click 'Save' button,",
|
||||
"inventory.taking.s5": "1.Click Sent button",
|
||||
"inventory.taking.s7": "Finished Inventory Taking",
|
||||
|
||||
"poapproved.slide.s1": "Select you want to approve PO",
|
||||
"poapproved.slide.s3": "1.Click Approve PO",
|
||||
"poapproved.slide.s5": "Finished PO Approved",
|
||||
|
||||
"doapproved.slide.s1": "Select you want to approve DO",
|
||||
"doapproved.slide.s3": "1.Select storage name",
|
||||
"doapproved.slide.s6": "Finished DO Approved",
|
||||
"doapproved.slide.s5": "1.Click 'More' button\n2.Click Approve DO",
|
||||
|
||||
"buyerapproved.slide.s1": "1.Select New Buyer Registration",
|
||||
"buyerapproved.slide.s3": "1.Click 'Approve'",
|
||||
|
||||
"product.slide.s1": "1.Click 'Products'",
|
||||
"product.slide.s2": "1.Click pencil button",
|
||||
"product.slide.s3": "1.Enter product info\n2.Click 'Save' button",
|
||||
|
||||
|
||||
"pd.slide.s1": "1.Click 'PDs'",
|
||||
"pd.slide.s5": "1.Click Sent button\n2.Click 'Ok' button",
|
||||
|
||||
"dodelivery.slide.s1": "Select DO, want to delivery",
|
||||
|
||||
"Login": "Login",
|
||||
"Signup": "Signup",
|
||||
|
||||
"forget.pass" : "Forgot Password?",
|
||||
"manual.confirm": "Are you sure want to delete?",
|
||||
|
||||
"offline.status":"Offline, unable to connect to server!",
|
||||
|
||||
"report.title":"Reports",
|
||||
"report.user_delete_confirm":"Delete this report user?",
|
||||
"report.user.search":"Search",
|
||||
"report.users.title":"{0} Users",
|
||||
|
||||
"announcement.title":"Announcements",
|
||||
"announcement.form.title":"Announcement",
|
||||
"announcement.name":"Subject",
|
||||
"announcement.name_empty":"Please enter subject",
|
||||
"announcement.delete_confirm":"Delete announcement?",
|
||||
"announcement.desc":"Description"
|
||||
}
|
||||
500
assets/local/localization_mu.json
Normal file
@@ -0,0 +1,500 @@
|
||||
{
|
||||
"btn.save":"သိမ်းဆည်းရန်",
|
||||
"btn.approve":"အတည်ပြုရန်",
|
||||
|
||||
"product": "ကုန်ပစ္စည်း",
|
||||
"price": "ဈေးနှုန်း",
|
||||
"volume": "လီတာ",
|
||||
"amount": "ပမာဏ",
|
||||
"storage" :"သိုလှောင်",
|
||||
|
||||
"welcome.price.updateinfo":"နောက်ဆုံး စျေးပြောင်းချိန် : {0}",
|
||||
"welcome.price.trend":"စျေးလှုပ်ရှားမှု",
|
||||
"welcome.price.detail":"အသေးစိတ်",
|
||||
|
||||
"products.title":"ဆီအမျိုးအစားများ",
|
||||
"prodcuts":"ကုန်ပစ္စည်းများ",
|
||||
"prices":"ဈေးနှုန်းများ",
|
||||
"product.confirm":"ဆီဈေးနှုန်းပြင်ရန်သေချာပြီလား?",
|
||||
"products.prices":"ဈေးနှုန်းများ",
|
||||
"products.gas":"ဆီအမျိုးအစား",
|
||||
"product.item":"ဆီအမျိုးအစား",
|
||||
"product.name":"ကုန်ပစ္စည်း အမည်",
|
||||
"product.name_empty":"ကျေးဇူးပြု၍ ကုန်ပစ္စည်းနာမည်ပေးပါ",
|
||||
"product.new_price":"စျေးနှုန်းအသစ်",
|
||||
"product.price_empty":"ကျေးဇူးပြု၍ စျေးနှုန်းကိုထည့်ပါ",
|
||||
"product.order":"အမှာစာ",
|
||||
"product.order_empty":"ကျေးဇူးပြု၍ အမှာစာကိုထည့်ပါ",
|
||||
"product.balance_qty":"ဆီအဝယ်ထုတ်ရန်\nလက်ကျန်အရေအတွက်",
|
||||
"product.qtys":"လက်ကျန်အရေအတွက်များ",
|
||||
"product.update.date":"ရက်စွဲ",
|
||||
"product.close": "ယခု ဆီအဝယ် ပိတ်ပါသည်။",
|
||||
"product.open": "ဆီအဝယ် ကို {0} မှ {1}\n{2} နေ့များတွင် ဖွင့်ပါသည်။",
|
||||
"product.purchase.order": "ဝယ်မည်",
|
||||
|
||||
"login.title":"အကောင့်ဒ်၀င်ရန်",
|
||||
"login.btn":"ဝင်မည်",
|
||||
"login.name":"နာမည်",
|
||||
"login.phone":"ဖုန်းနံပါတ်",
|
||||
"login.password":"စကားဝှက်",
|
||||
"login.confirm_password":"အတည်ပြုစကားဝှက်",
|
||||
"login.name_empty": "ကျေးဇူးပြု၍ နာမည်ထည့်ပေးပါ",
|
||||
"login.phone_empty": "ကျေးဇူးပြု၍ သင့်ဖုန်းနံပါတ်ထည့်ပါ",
|
||||
"login.phone_code": "'+ 959'သာစတင်သည်",
|
||||
"login.password_empty": "ကျေးဇူးပြု၍ စကားဝှက်ကိုရိုက်ထည့်ပါ",
|
||||
"login.password_size": "အနည်းဆုံးစာလုံး ၆ လုံး",
|
||||
"login.confirm_password_empty": "အတည်ပြုစကားဝှက်ကိုရိုက်ထည့်ပါ",
|
||||
"login.sms_empty":"ကျေးဇူးပြုပြီး sms ကုဒ်ကိုထည့်ပါ",
|
||||
"login.forgot_password":"စကားဝှက်ကိုမေ့နေပါသလား?",
|
||||
"login.email.title":"Account ပြန်ယူသော အီးမေးလ်",
|
||||
"login.email":"အီးမေးလ်",
|
||||
"login.email.empty":"ကျေးဇူးပြု၍အီးမေးလ်ထည့်ပါ",
|
||||
"login.email.add":"ထည့်ရန်",
|
||||
"login.email.skip":"ကျော်ရန်",
|
||||
|
||||
"sing.up":"အသစ်ပြုလုပ်မည်",
|
||||
"sing.title":"အသစ်ပြုလုပ်ရန်",
|
||||
|
||||
"change.password.title":"စကားဝှက်ကိုပြောင်းရန်",
|
||||
"change":"ပြောင်းမည်",
|
||||
"change.phone":"ဖုန်းနံပါတ်ပြောင်းရန်",
|
||||
"change.new.phone":"ဖုန်းနံပါတ်အသစ်",
|
||||
"change.phone_empty": "ကျေးဇူးပြု၍ ဖုန်းနံပါတ်အသစ်ထည့်ပါ",
|
||||
"change.email":"Account ပြန်ယူသော အီးမေးလ်",
|
||||
"change.pin.title":"ပင်နံပါတ်ထည့်ရန်",
|
||||
"change.pin":"ပင်နံပါတ်",
|
||||
"change.pin_empty":"ကျေးဇူးပြု၍ ပင်နံပါတ်ကိုရိုက်ထည့်ပါ",
|
||||
"change.pin_size":"စာလုံး ၆ လုံးဖြစ်ရမည်",
|
||||
"pin.add_btn":"ပင်ထည့်မည်",
|
||||
"pin.clear_btn":"ပင်ရှင်းမည် ",
|
||||
"pin.switch":"ပင်နံပါတ်ရှင်းရန်",
|
||||
|
||||
"forget.password":"စကားဝှက်ကိုပြင်ရန်",
|
||||
"forget.email":"အီးမေးလ်(သို့) ဖုန်းနံပါတ်",
|
||||
"forget.email.empty":"ကျေးဇူးပြု၍အီးမေးလ်(သို့)ဖုန်းနံပါတ်ထည့်ပါ",
|
||||
"forget.enter":"ဝင်မည်",
|
||||
|
||||
"reset.password.title":"စကားဝှက်ကိုပြန်လုပ်ရန်",
|
||||
"reset.sms":"SMS ကုဒ်",
|
||||
"reset.new_password":"စကားဝှကိအသစ်",
|
||||
"reset":"ပြင်မည်",
|
||||
|
||||
"reg.title":"ကိုယ်ရေးအချက်အလက်",
|
||||
"reg.biz_name":"လုပ်ငန်း အမည်",
|
||||
"reg.biz_address":"လုပ်ငန်း လိပ်စာ",
|
||||
"reg.biz_shops":"ဆိုင်အရေအတွက်",
|
||||
"reg.biz_shop_addresses":"ဆိုင်လိပ်စာများ",
|
||||
"reg.type_shop":"ကိုယ်ပိုင်",
|
||||
"reg.type_agent":"ကိုယ်စားလှယ်",
|
||||
"reg.status": "အခြေအနေ",
|
||||
"reg.table_product":"ကုန်ပစ္စည်း",
|
||||
"reg.table_storage_vol":"သိုလှောင်",
|
||||
"reg.table_sale_vol":"ရောင်းအား",
|
||||
"reg.quota": "နေ့စဉ်ခွဲတမ်း",
|
||||
"reg.max_quota": "အများဆုံးခွဲတမ်း",
|
||||
"reg.quota.used": "နေ့စဉ်\nအသုံးပြုပြီးခွဲတမ်း",
|
||||
"reg.max_quota.used": "အများဆုံး\nအသုံးပြုပြီးခွဲတမ်း",
|
||||
"reg.empty_biz_name": "Empty biz name",
|
||||
"reg.empty_biz_address":"Empty biz address",
|
||||
"reg.empty_shops":"Empty shops",
|
||||
"reg_info.nric_front": "မှတ်ပုံတင် ရှေ့ခြမ်း",
|
||||
"reg_info.nric_back": "မှတ်ပုံတင် နောက်ခြမ်း",
|
||||
"reg_info.title": "ကိုယ်ရေးအချက်အလက်",
|
||||
"reg.confirm":"မှတ်ပုံတင်သွင်းမည်လား?",
|
||||
"reg.date":"မှတ်ပုံတင်သည့် နေ့စွဲ",
|
||||
|
||||
"profile.title":"ပရိုဖိုင်",
|
||||
"profile.edit_title":"ပရိုဖိုင်ကိုပြုပြင်ရန်",
|
||||
"profile.name":"နာမည်",
|
||||
"profile.phone": "ဖုန်းနံပါတ်",
|
||||
"profile.language": "ဘာသာစကားများ",
|
||||
"profile.logout": "အကောင့်ထွက်ရန်",
|
||||
"profile.logout.confirm":"အကောင့်ထွက်ရန်သေချာပြီလား?",
|
||||
"profile.devices":"ဖုန်းမော်ဒယ်အမျိုးအစားများ",
|
||||
"profile.email":"အီးမေးလ်",
|
||||
"profile.privilege":"လုပ်ပိုင်ခွင့်",
|
||||
|
||||
"device.confirm":"ဒီဖုန်းမော်ဒယ်ကိုအတည်ပြုမည်လား?",
|
||||
"device.logout":"ဒီဖုန်းမော်ဒယ်ကိုထွက်ရန်သေချာပြီလား?",
|
||||
"device.set_primary":"ဒီဖုန်းမော်ဒယ်ကိုမူလမော်ဒယ်ထည့်ရန်သေချာပြီလား?",
|
||||
|
||||
"po.title":"ဆီအဝယ်များ",
|
||||
"po":"ဆီအဝယ်",
|
||||
"po.approval":"ကုန်ပစ္စည်းအမှာစာအချက်အလက်",
|
||||
"po.date" :"ဆီအဝယ် ရက်စွဲ",
|
||||
"po.number":"အဝယ် နံပါတ်",
|
||||
"po.name":"နာမည်",
|
||||
"po.po_num": "ဆီအဝယ် နံပါတ်",
|
||||
"po.biz":"လုပ်ငန်း အမည်",
|
||||
"po.status":"ဆီအဝယ် အခြေအနေ",
|
||||
"po.receipt":"လက်ခံဖြတ်ပိုင်းများ",
|
||||
"po.po_payment":"ဆီအဝယ် ပေးဆောင်ခြင်း",
|
||||
"po.po_payment_receipt":"ပေးဆောင်ခြင်းလက်ခံဖြတ်ပိုင်း",
|
||||
"po.storage_receipt":"သိုလှောင် လက်ခံဖြတ်ပိုင်း",
|
||||
"po.no.photo": "ပုံများရွေးချယ်ထားခြင်းမရှိပါ",
|
||||
"po.storage_charge" :"သိုလှောင်ခ",
|
||||
"po.product": "ကုန်ပစ္စည်း",
|
||||
"po.price": "ဈေးနှုန်း",
|
||||
"po.volume": "လီတာ",
|
||||
"po.balance.volume":"လက်ကျန်လီတာ",
|
||||
"po.amount": "ပမာဏ",
|
||||
"po.form.volume":"ကျေးဇူးပြု၍ လီတာပမာဏကိုထည့်ပါ",
|
||||
"po.comment":"မှတ်ချက်",
|
||||
"po.confirm":"ဆီအဝယ်တင်သွင်းမည်လား?",
|
||||
"po.approve.confirm":"ဆီဝယ်ခြင်းအတည်ပြုမည်လား?",
|
||||
"po.cancel.confirm":"ဆီဝယ်ခြင်းပယ်ဖျက်မည်လား?",
|
||||
"po.counts":"ဆီအဝယ် အရေအတွက်များ",
|
||||
"po.details":"အသေးစိတ်",
|
||||
"po.count":"အရေအတွက်",
|
||||
"po.balances":"ဆီအဝယ်လက်ကျန်အရေအတွက်",
|
||||
"po.info":"ဆီအဝယ်အချက်အလက်များ",
|
||||
"po.retrieved.amount":"ဆီထုတ်လက်ကျန်\nအရေအတွက်",
|
||||
"po.count.status":"ဆီအဝယ်\nအခြေအနေ",
|
||||
"po.avail.qty":"အဝယ်လက်ကျန်\nအရေအတွက်",
|
||||
"po.total_count":"စုစုပေါင်းအရေအတွက်",
|
||||
|
||||
"do.title":"ဆီအထုတ်များ",
|
||||
"do":"ဆီအထုတ်",
|
||||
"do.do_date":"ဆီအထုတ်\nတင်သွင်းသည့်ရက်စွဲ",
|
||||
"do.date": "ဆီအထုတ် ရက်စွဲ",
|
||||
"do.licence": "ယာဉ်မောင်းလိုင်စင်",
|
||||
"do.driver":"ယာဉ်မောင်း ",
|
||||
"do.car":"ကားနံပါတ်",
|
||||
"do.status": "ဆီအထုတ်များ အခြေအနေ",
|
||||
"do.delivery.status": "ဆီပေးပို့မှု အခြေအနေ",
|
||||
"do.delivery.init.time":"Delivery Initiated At",
|
||||
"do.do_num": "ဆီအထုတ် နံပါတ်",
|
||||
"do.type": "ဆီအထုတ်\nအမျိုးအစား",
|
||||
"do.name":"နာမည်",
|
||||
"do.biz":"လုပ်ငန်း အမည်",
|
||||
"do.form.date":"ကျေးဇူးပြု၍ ဆီအထုတ် နေ့စွဲရိုက်ထည့်ပါ",
|
||||
"do.form.licence":"ကျေးဇူးပြု၍ ကားလိုင်စင်ထည့်ပါ",
|
||||
"do.form.driver":"ကျေးဇူးပြု၍ ယာဉ်မောင်းအမည်ထည့်ပါ",
|
||||
"do.form.car":"ကျေးဇူးပြုပြီးကားနံပါတ်ထည့်ပါ",
|
||||
"do.product": "ကုန်ပစ္စည်း",
|
||||
"do.price": "ဈေးနှုန်း",
|
||||
"do.volume": "လီတာ",
|
||||
"do.amount": "ပမာဏ",
|
||||
"do.storage":"သိုလှောင်",
|
||||
"do.quantity":"အရေအတွက်",
|
||||
"do.form.volume":"ကျေးဇူးပြု၍ လီတာပမာဏကိုထည့်ပါ",
|
||||
"do.form.amount":"ကျေးဇူးပြု၍ ငွေပမာဏ ကိုထည့်ပါ",
|
||||
"do.po_qty":"အဝယ်\nအရေအတွက်",
|
||||
"do.po_balance_qty":"အဝယ်လက်ကျန်\nအရေအတွက်",
|
||||
"do.do_qty":"အထုတ်\nအရေအတွက်",
|
||||
"do.po_num":"အဝယ် နံပါတ်",
|
||||
"do.driver.image":"Driver Image",
|
||||
"do.driver.sign":"Driver Signature",
|
||||
"do.receipt":"ဆီအထုတ် လက်ခံဖြတ်ပိုင်း",
|
||||
"do.no.photo":"ပုံများရွေးချယ်ထားခြင်းမရှိပါ",
|
||||
"do.storage_charge" :"သိုလှောင်ခ",
|
||||
"do.storage_receipt":"သိုလှောင်လက်ခံဖြတ်ပိုင်း",
|
||||
"do.comment":"မှတ်ချက်",
|
||||
"do.confirm":"ဆီအထွက်တင်သွင်းမည်လား?",
|
||||
"do.approve.confirm":"ဆီထုတ်ခြင်းအတည်ပြုမည်လား?",
|
||||
"do.cancel.confirm":"ဆီဝယ်ခြင်းပယ်ဖျက်မည်လား?",
|
||||
"do.end.confirm":"ပေးပို့ခြင်းအဆုံးသတ်မည်လား?",
|
||||
"do.receipt.title":"ဆီအထုတ် လက်ခံဖြတ်ပိုင်း",
|
||||
"do.single":"အကုန်ထုတ်",
|
||||
"do.multiple": "ခွဲထုတ်",
|
||||
"do_qty":"ဆီအထုတ်အရေအတွက်",
|
||||
"do.product.title":"ဆီအထုတ်ကုန်ပစ္စည်း",
|
||||
"do.cancel":"ပယ်ဖျက်မည်",
|
||||
"do.enter":"ဝင်မည်",
|
||||
"do.qtys":"DO အရေအတွက်များ",
|
||||
"do.details":"အသေးစိတ်",
|
||||
"do.counts":"ဆီအထုတ် အရေအတွက်များ",
|
||||
"do.count":"အရေအတွက်",
|
||||
"do.products":"ဆီအထုတ် ကုန်ပစ္စည်းများ",
|
||||
"do.count.status":"ဆီအထုတ်\nအခြေအနေ",
|
||||
"do.total_count":"စုစုပေါင်းအရေအတွက်",
|
||||
|
||||
"storage.name":"သိုလှောင် အမည်",
|
||||
"storage.form.name":"ကျေးဇူးပြု၍ သိုလှောင် အမည်ကိုထည့်ပါ",
|
||||
"storage.date":"ရက်စွဲ",
|
||||
"storage.form.date":"ကျေးဇူးပြု၍ ရက်စွဲ ကိုထည့်ပါ",
|
||||
"storge": "သိုလှောင်",
|
||||
"storage.product.qty":"အရေအတွက်",
|
||||
"storage.delete_confirm":"သိုလှောင် ကိုဖျက်မည်လား?",
|
||||
|
||||
"name": "နာမည်",
|
||||
"form.name":"နာမည်ကိုရိုက်ထည့်ပေးပါ",
|
||||
|
||||
"input_sms": "SMS ကုဒ်ကိုရိုက်ထည့်ရန်",
|
||||
"sms.sms": "SMS ကုဒ်ကိုရိုက်ထည့်ပါ",
|
||||
"sms.empty":"ကျေးဇူးပြု၍ SMS Code ထည့်ပါ",
|
||||
"sms.enter":"ဝင်မည်",
|
||||
"singin": "ဝင်မည်",
|
||||
|
||||
"email.input": "အီးမေးလ် ကုဒ်ကိုရိုက်ထည့်ရန်",
|
||||
"email.code":"အီးမေးလ် ကုဒ်ကိုရိုက်ထည့်ပါ",
|
||||
"email.code_empty":"ကျေးဇူးပြု၍ အီးမေးလ်ကုဒ် ထည့်ပါ",
|
||||
|
||||
"inventory.takings":"စာရင်းများ",
|
||||
"inventory.take":"စာရင်းယူခြင်း",
|
||||
"inventory.item":"စာရင်း",
|
||||
"inventory.product": "ကုန်ပစ္စည်း",
|
||||
"inventory.storage": "သိုလှောင်",
|
||||
"inventory.quantity": "အရေအတွက်",
|
||||
"inventory.old.qty": "အဟောင်းအရေအတွက်",
|
||||
"inventory.new.qty": "အသစ်အရေအတွက်",
|
||||
"inventory.form.qty":"ကျေးဇူးပြု၍ အရေအတွက် ကိုထည့်ပါ",
|
||||
"inventory.confirm":"စာရင်းရယူခြင်းတင်သွင်းမည်လား?",
|
||||
|
||||
"pd":"ဆီအဝင်",
|
||||
"pd.product.title":"ဆီအဝင်ကုန်ပစ္စည်း",
|
||||
"pd.date" :"ရက်စွဲ",
|
||||
"pd.product": "ကုန်ပစ္စည်း",
|
||||
"pd.storage": "သိုလှောင်",
|
||||
"pd.quantity": "အရေအတွက်",
|
||||
"pd.form.quan":"ကျေးဇူးပြု၍ အရေအတွက် ကိုထည့်ပါ",
|
||||
"pd.confirm":"ဆီအဝင်အတည်ပြုမည်လား?",
|
||||
|
||||
"buyer.title":"ဝယ်ယူသူ",
|
||||
"buyer.type_biz":"လုပ်ငန်း အမျိုးအစား",
|
||||
"buyer.account_name":"အကောင့်ဒ် နာမည်",
|
||||
"buyer.phone_number":"ဖုန်းနံပါတ်",
|
||||
"buyer.quota":"ခွဲတမ်း",
|
||||
"buyer.max.quota":"အများဆုံးခွဲတမ်း",
|
||||
"buyer.name":"ဝယ်သူ",
|
||||
"buyer.product":"ကုန်ပစ္စည်း",
|
||||
"buyer.balQty":"လက်ကျန်လီတာ",
|
||||
"buyer.approve.confirm":"ဝယ်ယူသူအတည်ပြုရန်သေချာပြီလား?",
|
||||
"buyer.delete.confirm":"ဝယ်ယူသူပယ်ဖျက်မည်လား?",
|
||||
"buyer.allocate.quota.confirm":"ခွဲတမ်းသတ်မှတ်မည်လား?",
|
||||
|
||||
"term.agree_btn":"သဘောတူပါသည်",
|
||||
"term.iagree":"ကျွန်တော် သဘောတူပါသည်",
|
||||
|
||||
"noti.title":"အသိပေးချက်များ",
|
||||
"log.title":"မှတ်တမ်းများ",
|
||||
|
||||
"document.log.title":"မှတ်တမ်းများ",
|
||||
"document.date":"ရက်စွဲ",
|
||||
"document.by":"အားဖြင့်",
|
||||
"document.desc":"ဖော်ပြချက်",
|
||||
|
||||
"contact.title":"ဆက်သွယ်ရန်",
|
||||
"contact.phone.title":"ဖုန်းနံပါတ်ထည့်သွင်းရန်",
|
||||
"contact.phone":"ဖုန်းနံပါတ်",
|
||||
"contact.phone.confim":"ဖုန်းနံပါတ် '{0}' ကို ခေါ်မလား?",
|
||||
"contact.phone.empty":"ကျေးဇူးပြု၍ ဖုန်းနံပါတ်ထည့်ပါ",
|
||||
"contact.email":"အီးမေးလ်",
|
||||
"contact.email.configm":"အီးမေးလ် '{0}' ကိုပို့ မလား?",
|
||||
"contact.email.empty":"ကျေးဇူးပြု၍ အီးမေးလ်ထည့်ပါ",
|
||||
"contact.facebook":"ဖေ့စ်ဘွတ်ခ်",
|
||||
"contact.facebook.empty":"ကျေးဇူးပြုပြီး ဖေ့စ်ဘွတ်ခ် လင့်ခ် ကိုရိုက်ထည့်ပါ",
|
||||
"contact.google":"ဝဘ်ဆိုက်",
|
||||
"contact.google.empty":"ကျေးဇူးပြုပြီး ဝဘ်ဆိုက် လင့်ခ် ကိုရိုက်ထည့်ပါ",
|
||||
"contact.confrim":"ဆက်သွယ်ခြင်း ကိုပြင်မည်လား?",
|
||||
"contact.open.confrim":"'{0}' ကိုဖွင့် မလား?",
|
||||
"contact.bank.accounts":"ဘဏ်အကောင့်အချက်အလက်",
|
||||
"contact.address":"လိပ်စာ",
|
||||
"contact.delivery.phone":"သီလဝါ ဆီထုတ် ဖုန်းနံပါတ်",
|
||||
|
||||
|
||||
"term.title":"စည်းကမ်းချက်များ",
|
||||
"manual.title":"လက်စွဲစာအုပ်",
|
||||
"myreg.title":"ကိုယ်ရေး\nအချက်အလက်",
|
||||
"storage.title":"သိုလှောင်ကန်များ",
|
||||
"storage.item.title":"သိုလှောင်ကန်",
|
||||
"pd.title":"ဆီအဝင်များ",
|
||||
|
||||
"employee.title":"ဝန်ထမ်းများ",
|
||||
"employee.item.title":"ဝန်ထမ်း",
|
||||
"employee.phone":"ဖုန်းနံပါတ်",
|
||||
"employee.add":"ထည့်မည်",
|
||||
"employee.save":"သိမ်းဆည်းမည်",
|
||||
|
||||
"setting.title":"ချိန်ညှိချက်များ",
|
||||
"setting.confirm":"ချိန်ညှိချက်များ ကိုပြင်မည်လား?",
|
||||
|
||||
"users.title":"အသုံးပြုသူများ",
|
||||
"user.title":"အသုံးပြုသူ",
|
||||
"user.save":"သိမ်းဆည်းမည်",
|
||||
"user.block.confirm":"ဤအသုံးပြုသူအားတားဆီးရန်အတည်ပြုမည်လား?",
|
||||
"user.block_list":"တားမြစ်ထားသူများ",
|
||||
"user.unblock.confirm":"တားမြစ်ထားသူအားပယ်ဖျက်မည်လား?",
|
||||
|
||||
"banks.title":"ဘဏ်အကောင့်များ",
|
||||
"banks.edit.title":"ဘဏ်အကောင့်",
|
||||
"banks.name":"ဘဏ် အမည်",
|
||||
"banks.account.name":"ဘဏ်အကောင့် အမည်",
|
||||
"banks.account.number":"အကောင့် နံပါတ်",
|
||||
"banks.account.delete.confirmation":"Delete Bank Account?",
|
||||
|
||||
"delivery.title":"ပေးပို့ရန်များ",
|
||||
"delivery":"ပေးပို့ရန်",
|
||||
"delivery.confirm":"ပေးပို့ခြင်း အဆုံးသတ်မည်လား?",
|
||||
"delivery.detail":"အသေးစိတ်",
|
||||
"delivery.date":"ရက်စွဲ",
|
||||
"delivery.qty":"ပမာဏ",
|
||||
"delivery.do.title":"ဆီအထုတ်ပေးပို့ခြင်း",
|
||||
"delivery.do.details":"အသေးစိတ်",
|
||||
"delivery.do.counts":"ဆီအထုတ်အရေအတွက်များ",
|
||||
"delivery.do.count":"အရေအတွက်များ",
|
||||
"delivery.do.summary":"ဆီအထုတ်ပေးပို့ခြင်း\nအကျဉ်းချုပ်",
|
||||
"delivery.do.sum.counts":"ဆီအထုတ်ပေးပို့ခြင်း အကျဉ်းချုပ်",
|
||||
"delivery.days":"ရက်ပေါင်း",
|
||||
"delivery.summary":"ဆီအထုတ် အကျဉ်းချုပ်",
|
||||
"delivery.sum.amounts":"ဆီအထုတ် အကျဉ်းချုပ် ပမာဏများ",
|
||||
|
||||
"delivery.data.title":"ပေးပို့ရန်များ",
|
||||
|
||||
"delivery.detail.title":"{0} ပေးပို့ရန်များ",
|
||||
|
||||
"chart.daily.title":"နေ့စဉ် ခွဲတမ်း ({0} လီတာ)",
|
||||
"chart.max.title": "အများဆုံး ခွဲတမ်း ({0} လီတာ)",
|
||||
"chart.remaining": "လက်ကျန်",
|
||||
"chart.used": "အသုံးပြုပြီး",
|
||||
"chart.revenue":"ဝင်ငွေ",
|
||||
"chart.spending":"အသုံးစရိတ်",
|
||||
"chart.date":"ရက်စွဲ",
|
||||
"chart.30_days":"(နောက်ဆုံး ၃၀ရက်)",
|
||||
|
||||
"revenue.amounts":"ဝင်ငွေပမာဏများ",
|
||||
"revenue.date":"ရက်စွဲ",
|
||||
"revenue.amount":"ပမာဏ",
|
||||
"revenue.detail":"အသေးစိတ်",
|
||||
"revenue.detail.title":"{0} ဝင်ငွေပမာဏများ",
|
||||
|
||||
"spending.amounts":"အသုံးစရိတ် ပမာဏများ",
|
||||
"spending.detail.title":"{0} အသုံးစရိတ် ပမာဏများ",
|
||||
|
||||
"load": "လုပ်ဆောင်နေပါသည်...",
|
||||
|
||||
"Cancel": "မလုပ်နဲ့",
|
||||
"Ok": "အိုကေ",
|
||||
|
||||
"singup": "အကောင့်ဒ်အသစ်ပြုလုပ်ခြင်း",
|
||||
"login": "အကောင့်ဒ်၀င်ရန်",
|
||||
"buyer.reg": "ဝယ်ယူသူမှတ်ပုံတင်ခြင်း",
|
||||
"po.sub": "ဆီအဝယ် တင်သွင်းခြင်း",
|
||||
"do.sub": "ဆီအထုတ် တင်သွင်းခြင်း",
|
||||
"inventory.taking": "စာရင်းယူခြင်း",
|
||||
"do.approved": "ဆီအထုတ်အတည်ပြုခြင်း",
|
||||
"po.approved": "ဆီအဝယ်အတည်ပြုခြင်း",
|
||||
"buyer.approved": "ဝယ်ယူသူအတည်ပြုခြင်း",
|
||||
"purchase.delivery": "ကုန်ပစ္စည်းဝယ်ယူခြင်း",
|
||||
"do.delivery": "ကုန်ပစ္စည်းပို့ခြင်း",
|
||||
"product.price": "ဆီဈေးနှုန်း",
|
||||
|
||||
|
||||
"1.Select 'Signup' button": "၁. 'Signup'ကိုရွေးပါ",
|
||||
"2.Enter all data info": "၂.အချက်အလက်များကိုထည့်ပါ",
|
||||
"3.Click 'SIGNUP' button": "၃.'SIGNUP'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Enter SMS code": "၁.ကုဒ်နံပါတ်ရိုက်ထည့်ပါ",
|
||||
"2.Click 'Sign In' button": "၁.'ဝင်မည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"Click 'Ok' button": "'အိုကေ'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"2.Click 'Ok' button": "၂.'အိုကေ'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Select 'Login' button": "၁.'ဝင်မည်'ခလုတ်ကိုရွေးပါ",
|
||||
"2.Enter Phone Number and Password": "၂.ဖုန်းနံပါတ်နှင့်လျှို့ဝှက်နံပါတ်ကိုရိုက်ထည့်ပါ",
|
||||
"3.Click 'LOGIN' button": "၃.'ဝင်မည်' ခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Enter all data info": "၁.အချက်အလက်များကိုထည့်ပါ",
|
||||
"2.Click Sent button": "၂.Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Click Sent button": "၁.Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Click 'POs'": "၁. 'ဆီအဝယ်များ'ကိုနှိပ်ပါ",
|
||||
"1.Enter Volume and select Product": "၁.ပမာဏ နှင့် ကုန်ပစ္စည်းကိုရွေးပါ",
|
||||
"2.Click 'Save' button": "၂. 'သိမ်းဆည်းမည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"Finished DO Submission": "ဆီအထုတ်တင်ပြချက်ပြီးဆုံးသည်",
|
||||
"Click Storages": "သိုလှောင်ကန်များကို ရွေးပါ",
|
||||
"1.Enter storage name": "၁.သိုလှောင်ကန်အမည်ကိုရွေးပါ",
|
||||
"Click Inventory Takings": "စာရင်းယူခြင်းခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Select storage name , product and enter quantity": "၁.သိုလှောင်ကန်အမည်, ကုန်ပစ္စည်းနှင့် ပမာဏကိုထည့်ပါ",
|
||||
"Finished Inventory Taking": "စာရင်းယူခြင်းပြီးဆုံးသည်",
|
||||
"Select you want to approve PO": "အတည်ပြုမည့်ဆီအဝယ်ကိုရွေးပါ",
|
||||
"Click Approve PO": "1.Approve POကိုနှိပ်ပါ",
|
||||
"Finished PO Approved": " ဆီအဝယ်အတည်ပြုခြင်းပြီးဆုံးသည်",
|
||||
"Select you want to approve DO": "အတည်ပြုမည့်ဆီအထုတ်ကိုရွေးပါ",
|
||||
"Select storage name": "၁. သိုလှောင်ကန်အမည်ကိုရွေးပါ",
|
||||
"2.Click Approve DO": "၂.Approve DO ကိုနှိပ်ပါ",
|
||||
"Finished DO Approved": "ဆီအထုတ် အတည်ပြုခြင်းပြီးဆုံးသည်",
|
||||
"Select New Buyer Registration": "၁.New Buyer Registrationကိုရွေးပါ",
|
||||
"Click 'Approve'": "၁.'Approve'ကိုနှိပ်ပါ",
|
||||
"Click 'Products'": "'ဆီဈေးနှုန်း'ကိုနှိပ်ပါ",
|
||||
"Click pencil button": "ခဲတံခလုတ်ကိုနှိပ်ပါ",
|
||||
"1.Enter product info": "၁.ကုန်ပစ္စည်း အချက်အလက်များကိုထည့်ပါ",
|
||||
"Select DO, want to delivery": "ပို့မည့်ဆီအထုတ်ကိုရွေးပါ",
|
||||
"Click 'Initiate Delivery' button": "'Initiate Delivery' ခလုတ်ကိုနှိပ်ပါ",
|
||||
"Click 'Start Delivery'": "'Start Delivery'ကိုနှိပ်ပါ",
|
||||
"1.Click '+' button": "၁.'+'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"Click 'PDs'": "'ဆီအဝင်များ'ကိုနှိပ်ပါ" ,
|
||||
"Click 'Sent' button": "Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
|
||||
"1":"၁",
|
||||
"2":"၂",
|
||||
"3":"၃",
|
||||
|
||||
"signup.slide.initial":"၁. မြှားခလုတ်ကိုနှိပ်ပါ",
|
||||
"signup.slide.s1": "၁. 'အသစ်ပြုလုပ်ရန်'ကိုရွေးပါ\n၂. အချက်အလက်များကိုထည့်ပါ\n၃. 'အသစ်ပြုလုပ်မည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"signup.slide.s2" : "၁. ကုဒ်နံပါတ်ရိုက်ထည့်ပါ\n၂. 'ဝင်မည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"signup.slide.s3": "၁.'သဘောတူပါသည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"signup.slide.s4": "၁. 'အိုကေ'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"signup.slide.s5": "အကောင့်ဒ်အသစ်ပြုလုပ်ခြင်းပြီးဆုံးသည်",
|
||||
|
||||
"login.slide.s1":"၁. 'အကောင့်ဒ်၀င်ရန်'ခလုတ်ကိုရွေးပါ\n၂. ဖုန်းနံပါတ်နှင့်စကားဝှက်ကိုရိုက်ထည့်ပါ\n၃. 'ဝင်မည်' ခလုတ်ကိုနှိပ်ပါ",
|
||||
|
||||
"buyerreg.slide.s1":"၁. 'ကိုယ်ရေးအချက်အလက်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"buyerreg.slide.s2":"၁. အချက်အလက်များကိုထည့်ပါ\n၂. Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
"buyerreg.slide.s4":"ဝယ်သူမှတ်ပုံတင်ခြင်းပြီးဆုံးသည်",
|
||||
|
||||
"posub.slide.s1": "၁. 'ဆီအဝယ်များ'ကိုနှိပ်ပါ",
|
||||
"posub.slide.s2": "၁. '+'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"posub.slide.s3":"၁. '+'ခလုတ်ကိုနှိပ်ပါ\n၂. Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
"posub.slide.s4":"၁. ပမာဏ နှင့် ကုန်ပစ္စည်းကိုရွေးပါ\n၂. 'သိမ်းဆည်းမည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"posub.slide.s6": "ဆီအဝယ်တင်ပြချက်ပြီးဆုံးသည်",
|
||||
|
||||
"dosub.slide.s1": "၁.'More' ခလုတ်ကိုနှိပ်ပါ",
|
||||
"dosub.slide.s2": "၁.'Create DO'ကိုနှိပ်ပါ'",
|
||||
"dosub.slide.s4" : "၁. အချက်အလက်များကိုထည့်ပါ\n၂.Sentခလုတ်ကိုနှိပ်ပါ",
|
||||
"dosub.slide.s6": "ဆီအထုတ်တင်ပြချက်ပြီးဆုံးသည်",
|
||||
|
||||
"storage.slide.s1": "Click Storages",
|
||||
"storage.slide.s3" : "၁. သိုလှောင်ကန်အမည်ကိုရွေးပါ\n၂. 'သိမ်းဆည်းမည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
|
||||
"inventory.taking.s1": "Click Inventory Takings",
|
||||
"inventory.taking.s4":"၁.သိုလှောင်ကန်အမည်, ကုန်ပစ္စည်းနှင့် ပမာဏကိုထည့်ပါ\n၂. 'သိမ်းဆည်းမည်'ခလုတ်ကိုနှိပ်ပါ",
|
||||
"inventory.taking.s5": "1.Click Sent button",
|
||||
"inventory.taking.s7": "Finished Inventory Taking",
|
||||
|
||||
"poapproved.slide.s1": "Select you want to approve PO",
|
||||
"poapproved.slide.s3": "1.Click Approve PO",
|
||||
"poapproved.slide.s5": "Finished PO Approved",
|
||||
|
||||
"doapproved.slide.s1": "Select you want to approve DO",
|
||||
"doapproved.slide.s3": "1.Select storage name",
|
||||
"doapproved.slide.s6": "Finished DO Approved",
|
||||
"doapproved.slide.s5": "၁.'More' ခလုတ်ကိုနှိပ်ပါ\n၂.Approve DO ကိုနှိပ်ပါ",
|
||||
|
||||
"buyerapproved.slide.s1": "1.Select New Buyer Registration",
|
||||
"buyerapproved.slide.s3": "1.Click 'Approve'",
|
||||
|
||||
"product.slide.s1": "1.Click 'Products'",
|
||||
"product.slide.s2": "1.Click pencil button",
|
||||
"product.slide.s3": "1.Enter product info\n2.Click 'Save' button",
|
||||
|
||||
|
||||
"pd.slide.s1": "1.Click 'PDs'",
|
||||
"pd.slide.s5": "1.Click Sent button\n2.Click 'Ok' button",
|
||||
|
||||
"dodelivery.slide.s1": "Select DO, want to delivery",
|
||||
|
||||
"Signup": "အသစ်ပြုလုပ်ရန်",
|
||||
"Login": "အကောင့်ဒ်၀င်ရန်",
|
||||
"forget.pass" : "စကားဝှက်မေ့နေပါသလား?",
|
||||
"manual.confirm": "Are you sure want to delete?",
|
||||
|
||||
"offline.status":"အင်တာနက် ပိတ်ထားသည်, ဆာဗာကို ဆက်သွယ်၍ မရပါ!",
|
||||
|
||||
"report.title":"Reports",
|
||||
"report.user_delete_confirm":"ဤအစီရင်ခံစာအသုံးပြုသူအားဖျက်မည်လား?",
|
||||
"report.user.search":"Search",
|
||||
"report.users.title":"{0} အသုံးပြုသူများ",
|
||||
|
||||
"announcement.title":"ကြေငြာချက်များ",
|
||||
"announcement.form.title":"ကြေငြာချက်",
|
||||
"announcement.name":"အကြောင်းအရာ",
|
||||
"announcement.name_empty":"ကျေးဇူးပြု၍ အကြောင်းအရာ အမည်ပေးပါ",
|
||||
"announcement.delete_confirm":"ကြေငြာချက်ကိုဖျက်မည်လား?",
|
||||
"announcement.desc":"ဖော်ပြချက်"
|
||||
}
|
||||
BIN
assets/logo.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/logo1 (copy).png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/logo_title.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
assets/manual.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/message.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
0
assets/my_file.json
Normal file
BIN
assets/myan_flag.png
Executable file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/page.png
Executable file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/password.png
Normal file
|
After Width: | Height: | Size: 870 B |
BIN
assets/pay.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/pdo.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/phone.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/pin.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
assets/product.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/product_icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/quota.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/r.jpg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
assets/reg.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/report.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
assets/sales.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/status.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/storage.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/storage12.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/submission.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/term.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/truck.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/volume.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/whole.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
163
lib/app.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/buyer_model.dart';
|
||||
import 'package:fcs/model/delivery_model.dart';
|
||||
import 'package:fcs/model/manual_model.dart';
|
||||
import 'package:fcs/model/notification_model.dart';
|
||||
import 'package:fcs/model/pd_model.dart';
|
||||
import 'package:fcs/model/reg_model.dart';
|
||||
import 'package:fcs/model/report_model.dart';
|
||||
import 'package:fcs/model/storage_model.dart';
|
||||
import 'package:fcs/model/test_model.dart';
|
||||
import 'package:fcs/pages/email_page.dart';
|
||||
import 'package:fcs/pages/login_page.dart';
|
||||
|
||||
import 'model/announcement_model.dart';
|
||||
import 'model/chart_model.dart';
|
||||
import 'model/device_model.dart';
|
||||
import 'model/do_model.dart';
|
||||
import 'model/employee_model.dart';
|
||||
import 'model/language_model.dart';
|
||||
import 'model/log_model.dart';
|
||||
import 'model/main_model.dart';
|
||||
import 'model/po_model.dart';
|
||||
import 'model/product_model.dart';
|
||||
import 'model/report_user_model.dart';
|
||||
import 'model/user_model.dart';
|
||||
import 'pages/home_page.dart';
|
||||
import 'pages/splash.dart';
|
||||
import 'pages/term.dart';
|
||||
import 'pages/welcome_page.dart';
|
||||
import 'widget/localization/app_translations_delegate.dart';
|
||||
import 'widget/localization/transalation.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
@override
|
||||
_AppState createState() => _AppState();
|
||||
}
|
||||
|
||||
class _AppState extends State<App> {
|
||||
final MainModel mainModel = new MainModel();
|
||||
final UserModel userModel = new UserModel();
|
||||
final ProductModel productModel = new ProductModel();
|
||||
final EmployeeModel employeeModel = new EmployeeModel();
|
||||
final POSubmissionModel poSubmissionModel = new POSubmissionModel();
|
||||
final DOModel doModel = new DOModel();
|
||||
final LanguageModel lanuguageModel = new LanguageModel();
|
||||
final StorageModel storageModel = new StorageModel();
|
||||
final PDModel pdModel = new PDModel();
|
||||
final RegModel regModel = new RegModel();
|
||||
final BuyerModel buyerModel = new BuyerModel();
|
||||
final NotificationModel notificationModel = new NotificationModel();
|
||||
final ChartModel chartModel = new ChartModel();
|
||||
final DeliveryModel deliveryModel = new DeliveryModel();
|
||||
final ManualModel manualModel = new ManualModel();
|
||||
final TestModel testModel = new TestModel();
|
||||
final LogModel logModel = new LogModel();
|
||||
final PhoneDeviceModel phoneDeviceModel = new PhoneDeviceModel();
|
||||
final ReportModel reportModel = new ReportModel();
|
||||
final AnnouncementModel announcementModel = new AnnouncementModel();
|
||||
final ReportUserModel reportUserModel = new ReportUserModel();
|
||||
|
||||
AppTranslationsDelegate _newLocaleDelegate;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_newLocaleDelegate = AppTranslationsDelegate(newLocale: null);
|
||||
Translation().onLocaleChanged = onLocaleChange;
|
||||
mainModel
|
||||
..addModel(userModel)
|
||||
..addModel(employeeModel)
|
||||
..addModel(lanuguageModel)
|
||||
..addModel(storageModel)
|
||||
..addModel(regModel)
|
||||
..addModel(poSubmissionModel)
|
||||
..addModel(doModel)
|
||||
..addModel(productModel)
|
||||
..addModel(pdModel)
|
||||
..addModel(buyerModel)
|
||||
..addModel(notificationModel)
|
||||
..addModel(chartModel)
|
||||
..addModel(deliveryModel)
|
||||
..addModel(logModel)
|
||||
..addModel(manualModel)
|
||||
..addModel(phoneDeviceModel)
|
||||
..addModel(regModel)
|
||||
..addModel(announcementModel)
|
||||
..addModel(reportModel)
|
||||
..addModel(testModel)
|
||||
..addModel(reportUserModel);
|
||||
this.mainModel.init();
|
||||
}
|
||||
|
||||
void onLocaleChange(Locale locale) {
|
||||
setState(() {
|
||||
_newLocaleDelegate = AppTranslationsDelegate(newLocale: locale);
|
||||
});
|
||||
}
|
||||
|
||||
Map<String, WidgetBuilder> route(BuildContext context) {
|
||||
final routes = <String, WidgetBuilder>{
|
||||
'/': (_) => SplashScreen(),
|
||||
'/home': (_) => HomePage(),
|
||||
'/welcome': (context) => WelcomePage(),
|
||||
'/term': (context) => Term(
|
||||
agreePage: true,
|
||||
),
|
||||
'/login': (context) => LoginPage(),
|
||||
'/email': (context) => EmailPage()
|
||||
};
|
||||
return routes;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(builder: (context) => mainModel),
|
||||
ChangeNotifierProvider(builder: (context) => userModel),
|
||||
ChangeNotifierProvider(builder: (context) => productModel),
|
||||
ChangeNotifierProvider(builder: (context) => employeeModel),
|
||||
ChangeNotifierProvider(builder: (context) => poSubmissionModel),
|
||||
ChangeNotifierProvider(builder: (context) => doModel),
|
||||
ChangeNotifierProvider(builder: (context) => storageModel),
|
||||
ChangeNotifierProvider(builder: (context) => pdModel),
|
||||
ChangeNotifierProvider(builder: (context) => lanuguageModel),
|
||||
ChangeNotifierProvider(builder: (context) => regModel),
|
||||
ChangeNotifierProvider(builder: (context) => buyerModel),
|
||||
ChangeNotifierProvider(builder: (context) => notificationModel),
|
||||
ChangeNotifierProvider(builder: (context) => chartModel),
|
||||
ChangeNotifierProvider(builder: (context) => deliveryModel),
|
||||
ChangeNotifierProvider(builder: (context) => manualModel),
|
||||
ChangeNotifierProvider(builder: (context) => logModel),
|
||||
ChangeNotifierProvider(builder: (context) => deliveryModel),
|
||||
ChangeNotifierProvider(builder: (context) => phoneDeviceModel),
|
||||
ChangeNotifierProvider(builder: (context) => reportModel),
|
||||
ChangeNotifierProvider(builder: (context) => announcementModel),
|
||||
ChangeNotifierProvider(builder: (context) => reportUserModel),
|
||||
ChangeNotifierProvider(
|
||||
builder: (context) => testModel,
|
||||
),
|
||||
],
|
||||
child: Consumer<LanguageModel>(
|
||||
builder: (BuildContext context, LanguageModel value, Widget child) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Ok Energy',
|
||||
routes: route(context),
|
||||
theme: ThemeData(accentColor: Colors.black),
|
||||
localizationsDelegates: [
|
||||
_newLocaleDelegate,
|
||||
//provides localised strings
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
//provides RTL support
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: Translation().supportedLocales());
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
119
lib/charts/bar_chart.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:charts_flutter/flutter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/charts/qtyby_customer_table.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/product_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
class BarChart extends StatefulWidget {
|
||||
@override
|
||||
_BarChartState createState() => _BarChartState();
|
||||
}
|
||||
|
||||
class _BarChartState extends State<BarChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
List<POChartData> chartSummary = new List();
|
||||
List<charts.Series<POChartData, String>> series;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var chartModel = Provider.of<ChartModel>(context, listen: false);
|
||||
this.chartSummary = chartModel.chartSummary;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
if (this.chartSummary.isNotEmpty) {
|
||||
this.chartSummary.forEach((s) {
|
||||
productModel.products.forEach((p) {
|
||||
if (p.id == s.productID) {
|
||||
s.displayOrder = p.displayOrder;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.chartSummary
|
||||
.sort((s1, s2) => s1.displayOrder.compareTo(s2.displayOrder));
|
||||
}
|
||||
List<charts.Series<POChartData, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: this.chartSummary,
|
||||
domainFn: (POChartData series, _) => series.productName,
|
||||
measureFn: (POChartData series, _) => series.balanceQty,
|
||||
colorFn: (POChartData series, _) =>
|
||||
charts.ColorUtil.fromDartColor(series.getColor),
|
||||
labelAccessorFn: (POChartData series, _) =>
|
||||
'${numberFormatter.format(series.balanceQty)}'),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, 'product.balance_qty',
|
||||
color: primaryColor, fontSize: 16),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
_load();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.BarChart(
|
||||
series,
|
||||
animate: true,
|
||||
vertical: false,
|
||||
defaultRenderer: new charts.BarRendererConfig(
|
||||
barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
labelPosition: charts.BarLabelPosition.auto,
|
||||
),
|
||||
),
|
||||
selectionModels: [
|
||||
SelectionModelConfig(changedListener: (SelectionModel model) {
|
||||
final selectedDatum = model.selectedDatum;
|
||||
if (selectedDatum.isNotEmpty) {
|
||||
selectedDatum.forEach((charts.SeriesDatum datumPair) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => QtyByCustomerTable(
|
||||
poChartData: datumPair.datum,
|
||||
)),
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
var _s = await chartModel.loadSummary();
|
||||
setState(() {
|
||||
this.chartSummary = _s ?? [];
|
||||
});
|
||||
}
|
||||
}
|
||||
83
lib/charts/delivery_do_line.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'delivery_do_line_detail.dart';
|
||||
|
||||
class DODeliveryLineChart extends StatefulWidget {
|
||||
@override
|
||||
_DODeliveryLineChartState createState() => _DODeliveryLineChartState();
|
||||
}
|
||||
|
||||
class _DODeliveryLineChartState extends State<DODeliveryLineChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<Data, DateTime>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getDeliveryDo(),
|
||||
domainFn: (Data series, _) => series.date,
|
||||
measureFn: (Data series, _) => series.count,
|
||||
colorFn: (_, __) => charts.ColorUtil.fromDartColor(primaryColor),
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "delivery.do.title", color: primaryColor, fontSize: 16),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"delivery.do.details",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => DODeliveryLineDetail()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.TimeSeriesChart(
|
||||
series,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
85
lib/charts/delivery_do_line_detail.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class DODeliveryLineDetail extends StatefulWidget {
|
||||
const DODeliveryLineDetail();
|
||||
@override
|
||||
_DODeliveryLineDetailState createState() => _DODeliveryLineDetailState();
|
||||
}
|
||||
|
||||
class _DODeliveryLineDetailState extends State<DODeliveryLineDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'delivery.do.counts',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "delivery.date")),
|
||||
MyDataColumn(label: LocalText(context, "delivery.do.count")),
|
||||
],
|
||||
rows: getProductRow(chartModel.revenue.getDeliveryDo()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<Data> doList) {
|
||||
return doList.map((d) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(dateFormatter.format(d.date), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
numberFormatter.format(d.count),
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
79
lib/charts/delivery_do_summary.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'delivery_do_summary_details.dart';
|
||||
|
||||
class DeliveryDoSummaryChart extends StatefulWidget {
|
||||
@override
|
||||
_DeliveryDoSummaryChartState createState() => _DeliveryDoSummaryChartState();
|
||||
}
|
||||
|
||||
class _DeliveryDoSummaryChartState extends State<DeliveryDoSummaryChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<Data, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getDeliveryDoSummary(),
|
||||
domainFn: (Data series, _) => "${series.totalDay}days",
|
||||
measureFn: (Data series, _) => series.totalCount,
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.totalCount)}'),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "delivery.do.summary",
|
||||
color: primaryColor, fontSize: 16),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"delivery.do.details",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => DeliveryDoSummaryDetail()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.BarChart(
|
||||
series,
|
||||
animate: true,
|
||||
vertical: false,
|
||||
defaultRenderer: new charts.BarRendererConfig(
|
||||
barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
labelPosition: charts.BarLabelPosition.auto,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
86
lib/charts/delivery_do_summary_details.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class DeliveryDoSummaryDetail extends StatefulWidget {
|
||||
const DeliveryDoSummaryDetail();
|
||||
@override
|
||||
_DeliveryDoSummaryDetailState createState() =>
|
||||
_DeliveryDoSummaryDetailState();
|
||||
}
|
||||
|
||||
class _DeliveryDoSummaryDetailState extends State<DeliveryDoSummaryDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'delivery.do.sum.counts',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "delivery.days")),
|
||||
MyDataColumn(label: LocalText(context, "delivery.do.count")),
|
||||
],
|
||||
rows: getProductRow(chartModel.revenue.getDeliveryDoSummary()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<Data> doList) {
|
||||
return doList.map((d) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(d.totalDay.toString(), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
numberFormatter.format(d.totalCount),
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
107
lib/charts/delivery_line.dart
Normal file
@@ -0,0 +1,107 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'delivery_line_data.dart';
|
||||
|
||||
class DeliveryBarChart extends StatefulWidget {
|
||||
@override
|
||||
_DeliveryBarChartState createState() => _DeliveryBarChartState();
|
||||
}
|
||||
|
||||
class _DeliveryBarChartState extends State<DeliveryBarChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<Data, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getDelivery(),
|
||||
domainFn: (Data series, _) =>
|
||||
"${series.date.day}-${series.date.month}-${series.date.year}",
|
||||
measureFn: (Data series, _) => series.amount,
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.amount)}'),
|
||||
];
|
||||
|
||||
List<charts.Series<Data, DateTime>> seriesLine = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getDelivery(),
|
||||
domainFn: (Data series, _) => series.date,
|
||||
measureFn: (Data series, _) => series.amount,
|
||||
colorFn: (_, __) => charts.ColorUtil.fromDartColor(primaryColor),
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.amount)}',
|
||||
),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "delivery", color: primaryColor, fontSize: 16),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"delivery.detail",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => DeliveryBarData()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.TimeSeriesChart(
|
||||
seriesLine,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: charts.BarChart(
|
||||
// series,
|
||||
// animate: true,
|
||||
// vertical: true,
|
||||
// defaultRenderer: new charts.BarRendererConfig(
|
||||
// barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
// labelPosition: charts.BarLabelPosition.auto,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
92
lib/charts/delivery_line_data.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'delivery_line_detail.dart';
|
||||
|
||||
class DeliveryBarData extends StatefulWidget {
|
||||
const DeliveryBarData();
|
||||
@override
|
||||
_DeliveryBarDataState createState() => _DeliveryBarDataState();
|
||||
}
|
||||
|
||||
class _DeliveryBarDataState extends State<DeliveryBarData> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'delivery.data.title',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "delivery.date")),
|
||||
MyDataColumn(label: LocalText(context, "delivery.qty"),numeric: true),
|
||||
],
|
||||
rows: getProductRow(chartModel.revenue.getDelivery()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<Data> doList) {
|
||||
return doList.map((d) {
|
||||
var r = MyDataRow(
|
||||
onSelectChanged: (bool selected) async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => DeliveryBarDetail(d.date)),
|
||||
);
|
||||
},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(dateFormatter.format(d.date), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
NumberCell(d.amount)
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
94
lib/charts/delivery_line_detail.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/do_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/do.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class DeliveryBarDetail extends StatefulWidget {
|
||||
final DateTime date;
|
||||
const DeliveryBarDetail(this.date);
|
||||
@override
|
||||
_DeliveryBarDetailState createState() => _DeliveryBarDetailState();
|
||||
}
|
||||
|
||||
class _DeliveryBarDetailState extends State<DeliveryBarDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd-MMM-yyyy');
|
||||
bool _isLoading = false;
|
||||
List<DOSubmission> dos = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
DOModel dOModel = Provider.of<DOModel>(context, listen: false);
|
||||
dOModel.getDOForDelivery(widget.date).then((dos) {
|
||||
setState(() {
|
||||
this.dos = dos;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'delivery.detail.title',
|
||||
translationVariables: [dateFormatter.format(widget.date)],
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 5),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 3),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 20,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "do.name")),
|
||||
MyDataColumn(label: LocalText(context, "do.do_num")),
|
||||
MyDataColumn(label: LocalText(context, "do.quantity"),numeric: true),
|
||||
],
|
||||
rows: getProductRow(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow() {
|
||||
return dos.map((d) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(d.userName, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(d.doNumber, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
NumberCell(d.totalQty)
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
78
lib/charts/delivery_summary.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'delivery_summary_detail.dart';
|
||||
|
||||
class DeliverySummary extends StatefulWidget {
|
||||
@override
|
||||
_DeliverySummaryState createState() => _DeliverySummaryState();
|
||||
}
|
||||
|
||||
class _DeliverySummaryState extends State<DeliverySummary> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<Data, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getDeliverySummary(),
|
||||
domainFn: (Data series, _) => "${series.totalDay}days",
|
||||
measureFn: (Data series, _) => series.totalAmount,
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.totalAmount)}'),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "delivery.summary",
|
||||
color: primaryColor, fontSize: 16),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"delivery.detail",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (_) => DeliverySummaryDetail()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.BarChart(
|
||||
series,
|
||||
animate: true,
|
||||
vertical: false,
|
||||
defaultRenderer: new charts.BarRendererConfig(
|
||||
barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
labelPosition: charts.BarLabelPosition.auto,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
85
lib/charts/delivery_summary_detail.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class DeliverySummaryDetail extends StatefulWidget {
|
||||
const DeliverySummaryDetail();
|
||||
@override
|
||||
_DeliverySummaryDetailState createState() => _DeliverySummaryDetailState();
|
||||
}
|
||||
|
||||
class _DeliverySummaryDetailState extends State<DeliverySummaryDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'delivery.sum.amounts',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "delivery.days")),
|
||||
MyDataColumn(label: LocalText(context, "delivery.amount")),
|
||||
],
|
||||
rows: getProductRow(chartModel.revenue.getDeliverySummary()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<Data> doList) {
|
||||
return doList.map((d) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(d.totalDay.toString(), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
numberFormatter.format(d.totalAmount),
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
156
lib/charts/do_line.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po_do_count.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'do_line_detail.dart';
|
||||
|
||||
class DOLineChart extends StatefulWidget {
|
||||
@override
|
||||
_DOLineChartState createState() => _DOLineChartState();
|
||||
}
|
||||
|
||||
class _DOLineChartState extends State<DOLineChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<CountData, DateTime>> series = [
|
||||
charts.Series(
|
||||
id: "pending",
|
||||
data: chartModel.podoCount.getDODataCounts("pending"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "approved",
|
||||
data: chartModel.podoCount.getDODataCounts("approved"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "canceled",
|
||||
data: chartModel.podoCount.getDODataCounts("canceled"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.gray.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "rejected",
|
||||
data: chartModel.podoCount.getDODataCounts("rejected"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "expired",
|
||||
data: chartModel.podoCount.getDODataCounts("expired"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.purple.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 5, 2, 5],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "closed",
|
||||
data: chartModel.podoCount.getDODataCounts("closed"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.indigo.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 5, 2, 5],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "do", color: primaryColor, fontSize: 16),
|
||||
LocalText(context, 'chart.30_days',
|
||||
color: primaryColor, fontSize: 14)
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"do.details",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => DOLineDetail()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.TimeSeriesChart(
|
||||
series,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
behaviors: [
|
||||
new charts.SeriesLegend(
|
||||
position: charts.BehaviorPosition.end,
|
||||
outsideJustification:
|
||||
charts.OutsideJustification.middleDrawArea,
|
||||
entryTextStyle: charts.TextStyleSpec(
|
||||
color: charts.Color(r: 127, g: 63, b: 191),
|
||||
fontFamily: 'Georgia',
|
||||
fontSize: 11),
|
||||
)
|
||||
],
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
121
lib/charts/do_line_detail.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po_do_count.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class DOLineDetail extends StatefulWidget {
|
||||
const DOLineDetail();
|
||||
@override
|
||||
_DOLineDetailState createState() => _DOLineDetailState();
|
||||
}
|
||||
|
||||
class _DOLineDetailState extends State<DOLineDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'do.counts',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "chart.date"),
|
||||
LocalText(context, "do.total_count")
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: getRowTotalCountWidget(
|
||||
chartModel.podoCount.getDOTotalCounts()),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getRowTotalCountWidget(List<TotalCountData> data) {
|
||||
return data.map((d) {
|
||||
return Container(
|
||||
child: ExpansionTile(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(dateFormatter.format(d.date), style: textStyle),
|
||||
Text(numberFormatter.format(d.totalCount), style: textStyle),
|
||||
],
|
||||
),
|
||||
children: <Widget>[
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "do.count.status")),
|
||||
MyDataColumn(label: LocalText(context, "do.count")),
|
||||
],
|
||||
rows: getStatusRow(d.detailCountsList),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<MyDataRow> getStatusRow(List<CountData> doList) {
|
||||
doList.sort((a, b) => a.status.compareTo(b.status));
|
||||
return doList.map((d) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(d.status, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
numberFormatter.format(d.count),
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
107
lib/charts/lines.dart
Normal file
@@ -0,0 +1,107 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../model/product_model.dart';
|
||||
|
||||
typedef void ProductClick(DateTime date, Map<String, int> measures);
|
||||
|
||||
class ProductsChart extends StatelessWidget {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
final ChartData chartData;
|
||||
final ProductClick productClick;
|
||||
const ProductsChart(this.chartData, {Key key, this.productClick})
|
||||
: super(key: key);
|
||||
factory ProductsChart.fromModel(ProductModel productModel,
|
||||
{ProductClick productClick}) {
|
||||
return new ProductsChart(_createData(productModel),
|
||||
productClick: productClick);
|
||||
}
|
||||
|
||||
static ChartData _createData(ProductModel productModel) {
|
||||
List<charts.Series<TimeSeriesSales, DateTime>> list = [];
|
||||
var min = 9999, max = 0;
|
||||
productModel.products.forEach((p) {
|
||||
List<TimeSeriesSales> data = [];
|
||||
if (p.priceHistory != null) {
|
||||
var dateKeys = {};
|
||||
p.priceHistory.entries.forEach((e) {
|
||||
dateKeys[DateTime.parse(e.key)] = e.value;
|
||||
});
|
||||
|
||||
var sortedKeys = dateKeys.keys.toList()..sort((a, b) => b.compareTo(a));
|
||||
sortedKeys.forEach((k) {
|
||||
var v = dateKeys[k];
|
||||
data.add(new TimeSeriesSales(k, v));
|
||||
if (v < min) min = v;
|
||||
if (v > max) max = v;
|
||||
});
|
||||
}
|
||||
|
||||
list.add(new charts.Series<TimeSeriesSales, DateTime>(
|
||||
id: p.name,
|
||||
colorFn: (_, __) => charts.ColorUtil.fromDartColor(Color(p.color)),
|
||||
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
||||
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
||||
data: data,
|
||||
labelAccessorFn: (TimeSeriesSales series, _) =>
|
||||
'${numberFormatter.format(series.sales)}',
|
||||
measureFormatterFn: (TimeSeriesSales series, _) => (n) => "s",
|
||||
));
|
||||
});
|
||||
var chartData = ChartData(list, min, max);
|
||||
return chartData;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return charts.TimeSeriesChart(
|
||||
chartData.seriesList,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
selectionModels: [
|
||||
new charts.SelectionModelConfig(
|
||||
type: charts.SelectionModelType.info,
|
||||
updatedListener: _onSelectionChanged,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_onSelectionChanged(charts.SelectionModel model) {
|
||||
final selectedDatum = model.selectedDatum;
|
||||
if (selectedDatum.isNotEmpty) {
|
||||
var _time = selectedDatum.first.datum.time;
|
||||
Map<String, int> _measures = <String, int>{};
|
||||
|
||||
selectedDatum.forEach((charts.SeriesDatum datumPair) {
|
||||
_measures[datumPair.series.displayName] = datumPair.datum.sales;
|
||||
});
|
||||
if (productClick != null) {
|
||||
productClick(_time, _measures);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TimeSeriesSales {
|
||||
final DateTime time;
|
||||
final int sales;
|
||||
|
||||
TimeSeriesSales(this.time, this.sales);
|
||||
}
|
||||
|
||||
class ChartData {
|
||||
final List<charts.Series> seriesList;
|
||||
final num min, max;
|
||||
ChartData(this.seriesList, this.min, this.max);
|
||||
}
|
||||
134
lib/charts/po_balance_chart.dart
Normal file
@@ -0,0 +1,134 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:charts_flutter/flutter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/charts/po_balance_table.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/product_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
class POBalanceChart extends StatefulWidget {
|
||||
@override
|
||||
_POBalanceChartState createState() => _POBalanceChartState();
|
||||
}
|
||||
|
||||
class _POBalanceChartState extends State<POBalanceChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
List<POChartData> chartSummary = new List();
|
||||
List<charts.Series<POChartData, String>> series;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var chartModel = Provider.of<ChartModel>(context, listen: false);
|
||||
if (mounted) {
|
||||
load(chartModel);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load(ChartModel chartModel) async {
|
||||
var _u = await chartModel.loadPOBalancesForBuyer();
|
||||
if (_u == null) return;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
this.chartSummary = _u;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var productModel = Provider.of<ProductModel>(context);
|
||||
if (this.chartSummary.isNotEmpty) {
|
||||
this.chartSummary.forEach((s) {
|
||||
productModel.products.forEach((p) {
|
||||
if (p.id == s.productID) {
|
||||
s.displayOrder = p.displayOrder;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this
|
||||
.chartSummary
|
||||
.sort((s1, s2) => s1.displayOrder.compareTo(s2.displayOrder));
|
||||
}
|
||||
List<charts.Series<POChartData, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: this.chartSummary,
|
||||
domainFn: (POChartData series, _) => series.productName,
|
||||
measureFn: (POChartData series, _) => series.balanceQty,
|
||||
colorFn: (POChartData series, _) =>
|
||||
charts.ColorUtil.fromDartColor(series.getColor),
|
||||
labelAccessorFn: (POChartData series, _) =>
|
||||
'${numberFormatter.format(series.balanceQty)}'),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, 'po.balances',
|
||||
color: primaryColor, fontSize: 16),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
_load();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.BarChart(
|
||||
series,
|
||||
animate: true,
|
||||
vertical: false,
|
||||
defaultRenderer: new charts.BarRendererConfig(
|
||||
barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
labelPosition: charts.BarLabelPosition.auto,
|
||||
),
|
||||
),
|
||||
selectionModels: [
|
||||
SelectionModelConfig(changedListener: (SelectionModel model) {
|
||||
final selectedDatum = model.selectedDatum;
|
||||
if (selectedDatum.isNotEmpty) {
|
||||
selectedDatum.forEach((charts.SeriesDatum datumPair) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => POBalanceTable(
|
||||
poChartData: datumPair.datum,
|
||||
)),
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
var _s = await chartModel.loadPOBalancesForBuyer();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
this.chartSummary = _s ?? [];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
106
lib/charts/po_balance_table.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class POBalanceTable extends StatefulWidget {
|
||||
final POChartData poChartData;
|
||||
const POBalanceTable({Key key, this.poChartData}) : super(key: key);
|
||||
@override
|
||||
_POBalanceTableState createState() => _POBalanceTableState();
|
||||
}
|
||||
|
||||
class _POBalanceTableState extends State<POBalanceTable> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
List<POChartData> chartUser = new List();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var chartModel = Provider.of<ChartModel>(context, listen: false);
|
||||
if (mounted) {
|
||||
load(chartModel);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load(ChartModel chartModel) async {
|
||||
var _u = await chartModel.loadPOBalProductsForBuyer();
|
||||
setState(() {
|
||||
this.chartUser = _u;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<POChartData> data = this
|
||||
.chartUser
|
||||
.where((u) => u.productName == widget.poChartData.productName)
|
||||
.toList();
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("product.qtys"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(fontSize: 18)
|
||||
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
headingRowHeight: 40,
|
||||
columnSpacing: 40,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "buyer.name")),
|
||||
MyDataColumn(label: LocalText(context, "buyer.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "buyer.balQty"), numeric: true),
|
||||
],
|
||||
rows: getProductRow(data),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<POChartData> poLines) {
|
||||
return poLines.map((p) {
|
||||
return MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(p.userName, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(
|
||||
NumberCell(p.balanceQty),
|
||||
//new Text(numberFormatter.format(p.balanceQty), style: textStyle),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
98
lib/charts/po_balanceby_buyer.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
class POBalanceChart_ extends StatefulWidget {
|
||||
@override
|
||||
_POBalanceChartState createState() => _POBalanceChartState();
|
||||
|
||||
|
||||
}
|
||||
|
||||
class _POBalanceChartState extends State<POBalanceChart_> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
List<POBuyerData> chartSummary = new List();
|
||||
List<charts.Series<POBuyerData, String>> series;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var chartModel = Provider.of<ChartModel>(context, listen: false);
|
||||
if (mounted) {
|
||||
load(chartModel);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load(ChartModel chartModel) async {
|
||||
var _u = await chartModel.loadPOBalancesForBuyer_();
|
||||
if (_u == null) return;
|
||||
setState(() {
|
||||
this.chartSummary = _u;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<charts.Series<POBuyerData, String>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: this.chartSummary,
|
||||
domainFn: (POBuyerData series, _) => series.status,
|
||||
measureFn: (POBuyerData series, _) => series.amount,
|
||||
colorFn: (POBuyerData series, _) =>
|
||||
charts.ColorUtil.fromDartColor(series.getColor),
|
||||
labelAccessorFn: (POBuyerData series, _) =>
|
||||
'${numberFormatter.format(series.amount)}'),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, 'po.balances',
|
||||
color: primaryColor, fontSize: 16),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
color: primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
_load();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.BarChart(
|
||||
series,
|
||||
animate: true,
|
||||
vertical: false,
|
||||
defaultRenderer: new charts.BarRendererConfig(
|
||||
barRendererDecorator: new charts.BarLabelDecorator<String>(
|
||||
labelPosition: charts.BarLabelPosition.auto,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
var _s = await chartModel.loadPOBalancesForBuyer_();
|
||||
if (_s == null) return;
|
||||
setState(() {
|
||||
this.chartSummary = _s;
|
||||
});
|
||||
}
|
||||
}
|
||||
155
lib/charts/po_line.dart
Normal file
@@ -0,0 +1,155 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/charts/po_line_detail.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po_do_count.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
class POLineChart extends StatefulWidget {
|
||||
@override
|
||||
_POLineChartState createState() => _POLineChartState();
|
||||
}
|
||||
|
||||
class _POLineChartState extends State<POLineChart> {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
List<charts.Series<CountData, DateTime>> series = [
|
||||
charts.Series(
|
||||
id: "pending",
|
||||
data: chartModel.podoCount.getPODataCounts('pending'),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "approved",
|
||||
data: chartModel.podoCount.getPODataCounts("approved"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "canceled",
|
||||
data: chartModel.podoCount.getPODataCounts("canceled"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.gray.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "rejected",
|
||||
data: chartModel.podoCount.getPODataCounts("rejected"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.red.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 3, 2, 3],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "expired",
|
||||
data: chartModel.podoCount.getPODataCounts("expired"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.purple.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 5, 2, 5],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
charts.Series(
|
||||
id: "closed",
|
||||
data: chartModel.podoCount.getPODataCounts("closed"),
|
||||
domainFn: (CountData series, _) => series.date,
|
||||
measureFn: (CountData series, _) =>
|
||||
series.count == null ? 0 : series.count,
|
||||
colorFn: (_, __) => charts.MaterialPalette.indigo.shadeDefault,
|
||||
dashPatternFn: (_, __) => [8, 5, 2, 5],
|
||||
labelAccessorFn: (CountData series, _) =>
|
||||
'${numberFormatter.format(series.count)}',
|
||||
),
|
||||
];
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, "po", color: primaryColor, fontSize: 16),
|
||||
LocalText(context, 'chart.30_days',
|
||||
color: primaryColor, fontSize: 14)
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"po.details",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (_) => POLineDetail()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.TimeSeriesChart(
|
||||
series,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
behaviors: [
|
||||
new charts.SeriesLegend(
|
||||
position: charts.BehaviorPosition.end,
|
||||
outsideJustification:
|
||||
charts.OutsideJustification.middleDrawArea,
|
||||
entryTextStyle: charts.TextStyleSpec(
|
||||
color: charts.Color(r: 127, g: 63, b: 191),
|
||||
fontFamily: 'Georgia',
|
||||
fontSize: 11),
|
||||
)
|
||||
],
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
121
lib/charts/po_line_detail.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po_do_count.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class POLineDetail extends StatefulWidget {
|
||||
const POLineDetail();
|
||||
@override
|
||||
_POLineDetailState createState() => _POLineDetailState();
|
||||
}
|
||||
|
||||
class _POLineDetailState extends State<POLineDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: LocalText(
|
||||
context,
|
||||
'po.counts',
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, left: 20, right: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
LocalText(context, "chart.date"),
|
||||
LocalText(context, "po.total_count")
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: getRowTotalCountWidget(
|
||||
chartModel.podoCount.getPOTotalCounts()),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<Widget> getRowTotalCountWidget(List<TotalCountData> data) {
|
||||
return data.map((d) {
|
||||
return Container(
|
||||
child: ExpansionTile(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(dateFormatter.format(d.date), style: textStyle),
|
||||
Text(numberFormatter.format(d.totalCount), style: textStyle),
|
||||
],
|
||||
),
|
||||
children: <Widget>[
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "po.count.status")),
|
||||
MyDataColumn(label: LocalText(context, "po.count")),
|
||||
],
|
||||
rows: getStatusRow(d.detailCountsList),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<MyDataRow> getStatusRow(List<CountData> po) {
|
||||
po.sort((a, b) => a.status.compareTo(b.status));
|
||||
return po.map((p) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(p.status, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.count != null ? numberFormatter.format(p.count) : '0',
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
103
lib/charts/qtyby_customer_table.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/language_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class QtyByCustomerTable extends StatefulWidget {
|
||||
final POChartData poChartData;
|
||||
const QtyByCustomerTable({Key key, this.poChartData}) : super(key: key);
|
||||
@override
|
||||
_QtyByCustomerTableState createState() => _QtyByCustomerTableState();
|
||||
}
|
||||
|
||||
class _QtyByCustomerTableState extends State<QtyByCustomerTable> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
List<POChartData> chartUser = new List();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var chartModel = Provider.of<ChartModel>(context, listen: false);
|
||||
if (mounted) {
|
||||
load(chartModel);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> load(ChartModel chartModel) async {
|
||||
var _u = await chartModel.loadUsers();
|
||||
setState(() {
|
||||
this.chartUser = _u;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<POChartData> data = this
|
||||
.chartUser
|
||||
.where((u) => u.productName == widget.poChartData.productName)
|
||||
.toList();
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: Text(
|
||||
AppTranslations.of(context).text("product.qtys"),
|
||||
style: Provider.of<LanguageModel>(context).isEng
|
||||
? TextStyle(fontSize: 18)
|
||||
: TextStyle(fontSize: 18, fontFamily: 'MyanmarUnicode'),
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: MyDataTable(
|
||||
headingRowHeight: 40,
|
||||
columnSpacing: 40,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "buyer.name")),
|
||||
MyDataColumn(label: LocalText(context, "buyer.product")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "buyer.balQty"), numeric: true),
|
||||
],
|
||||
rows: getProductRow(data),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<POChartData> poLines) {
|
||||
return poLines.map((p) {
|
||||
return MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(p.userName, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(
|
||||
p.productName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
MyDataCell(NumberCell(p.balanceQty)),
|
||||
],
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
118
lib/charts/quota.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
/// Simple pie chart example.
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:fcs/vo/buyer.dart';
|
||||
import 'package:fcs/vo/product.dart';
|
||||
import 'package:fcs/widget/localization/app_translations.dart';
|
||||
|
||||
class QuotaChart extends StatelessWidget {
|
||||
static final numberFormatter = new NumberFormat("#,###");
|
||||
|
||||
final List<charts.Series> quotaSeries;
|
||||
final String title;
|
||||
|
||||
QuotaChart(this.quotaSeries, this.title);
|
||||
|
||||
factory QuotaChart.dailyQuota(
|
||||
BuildContext context, Buyer buyer, List<Product> products) {
|
||||
List<Quota> data = [];
|
||||
products.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
|
||||
products.forEach((p) {
|
||||
if (buyer.dailyQuotaUsedProducts.containsKey(p.id)) {
|
||||
int value = buyer.dailyQuotaUsedProducts[p.id];
|
||||
data.add(Quota(p.name, value, p.color));
|
||||
}
|
||||
});
|
||||
data.add(
|
||||
new Quota(AppTranslations.of(context).text("chart.remaining"),
|
||||
buyer.dailyQuota - buyer.dailyQuotaUsed, Colors.purple.value),
|
||||
);
|
||||
|
||||
return new QuotaChart(
|
||||
_createData(data),
|
||||
AppTranslations.of(context).text("chart.daily.title",
|
||||
translationVariables: [numberFormatter.format(buyer.dailyQuota)]));
|
||||
}
|
||||
|
||||
factory QuotaChart.maxQuota(
|
||||
BuildContext context, Buyer buyer, List<Product> products) {
|
||||
List<Quota> data = [];
|
||||
products.sort((p1, p2) => p1.displayOrder.compareTo(p2.displayOrder));
|
||||
products.forEach((p) {
|
||||
if (buyer.maxQuotaUsedProducts.containsKey(p.id)) {
|
||||
int value = buyer.maxQuotaUsedProducts[p.id];
|
||||
data.add(Quota(p.name, value, p.color));
|
||||
}
|
||||
});
|
||||
data.add(
|
||||
new Quota(AppTranslations.of(context).text("chart.remaining"),
|
||||
buyer.maxQuota - buyer.maxQuotaUsed, Colors.purple.value),
|
||||
);
|
||||
return new QuotaChart(
|
||||
_createData(data),
|
||||
AppTranslations.of(context).text("chart.max.title",
|
||||
translationVariables: [numberFormatter.format(buyer.maxQuota)]));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text(title),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
height: 200,
|
||||
child: charts.PieChart(
|
||||
quotaSeries,
|
||||
animate: false,
|
||||
behaviors: [
|
||||
new charts.DatumLegend(
|
||||
position: charts.BehaviorPosition.end,
|
||||
horizontalFirst: false,
|
||||
cellPadding: new EdgeInsets.only(right: 4.0, bottom: 4.0),
|
||||
entryTextStyle: charts.TextStyleSpec(
|
||||
color: charts.MaterialPalette.purple.shadeDefault,
|
||||
fontFamily: 'Georgia',
|
||||
fontSize: 11),
|
||||
)
|
||||
],
|
||||
defaultRenderer: new charts.ArcRendererConfig(
|
||||
arcWidth: 60,
|
||||
arcRendererDecorators: [
|
||||
new charts.ArcLabelDecorator(
|
||||
labelPosition: charts.ArcLabelPosition.auto,
|
||||
labelPadding: 0)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static List<charts.Series<Quota, String>> _createData(List<Quota> data) {
|
||||
return [
|
||||
new charts.Series<Quota, String>(
|
||||
id: 'Daily Quota',
|
||||
domainFn: (Quota quota, _) => "${quota.label}\n",
|
||||
measureFn: (Quota quota, _) => quota.quota,
|
||||
data: data,
|
||||
colorFn: (Quota quota, i) =>
|
||||
charts.ColorUtil.fromDartColor(Color(quota.color)),
|
||||
labelAccessorFn: (Quota row, _) =>
|
||||
'${numberFormatter.format(row.quota)}',
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class Quota {
|
||||
final String label;
|
||||
final int quota;
|
||||
final int color;
|
||||
|
||||
Quota(this.label, this.quota, this.color);
|
||||
}
|
||||
118
lib/charts/revenue_line.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
|
||||
import 'revenue_line_data.dart';
|
||||
|
||||
class RevenueLineChart extends StatefulWidget {
|
||||
@override
|
||||
_RevenueLineChartState createState() => _RevenueLineChartState();
|
||||
}
|
||||
|
||||
class _RevenueLineChartState extends State<RevenueLineChart> {
|
||||
static final numberFormatter = NumberFormat.compact();
|
||||
int actualChart = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
List<charts.Series<Data, DateTime>> series = [
|
||||
charts.Series(
|
||||
id: "Subscribers",
|
||||
data: chartModel.revenue.getData(),
|
||||
domainFn: (Data series, _) => series.date,
|
||||
measureFn: (Data series, _) => series.amount,
|
||||
colorFn: (_, __) => charts.ColorUtil.fromDartColor(primaryColor),
|
||||
labelAccessorFn: (Data series, _) =>
|
||||
'${numberFormatter.format(series.amount)}',
|
||||
),
|
||||
];
|
||||
|
||||
final moneyFormatter =
|
||||
new charts.BasicNumericTickFormatterSpec.fromNumberFormat(
|
||||
new NumberFormat.compact());
|
||||
|
||||
return Container(
|
||||
height: 200,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
mainModel.user.isOwnerAndAbove()
|
||||
? Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, 'chart.revenue',
|
||||
fontSize: 16, color: primaryColor),
|
||||
LocalText(context, 'chart.30_days',
|
||||
color: primaryColor, fontSize: 14),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, 'chart.spending',
|
||||
fontSize: 16, color: primaryColor),
|
||||
LocalText(context, 'chart.30_days',
|
||||
color: primaryColor, fontSize: 14)
|
||||
],
|
||||
),
|
||||
Text(
|
||||
"${chartModel.revenue.mapData == null ? "" : numberFormatter.format(chartModel.revenue.getTotal(actualChart))}",
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 17.0))
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
child: LocalText(
|
||||
context,
|
||||
"revenue.detail",
|
||||
color: secondaryColor,
|
||||
fontSize: 14,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (_) => RevenueLineData()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: charts.TimeSeriesChart(
|
||||
series,
|
||||
animate: true,
|
||||
defaultRenderer: new charts.LineRendererConfig(
|
||||
includePoints: true,
|
||||
),
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickFormatterSpec: moneyFormatter,
|
||||
tickProviderSpec: new charts.BasicNumericTickProviderSpec(
|
||||
zeroBound: false, desiredTickCount: 10),
|
||||
renderSpec: new charts.GridlineRendererSpec(
|
||||
lineStyle: charts.LineStyleSpec(
|
||||
dashPattern: [4, 4],
|
||||
))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
92
lib/charts/revenue_line_data.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/chart_model.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/revenue.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
import 'revenue_line_detail.dart';
|
||||
|
||||
class RevenueLineData extends StatefulWidget {
|
||||
const RevenueLineData();
|
||||
@override
|
||||
_RevenueLineDataState createState() => _RevenueLineDataState();
|
||||
}
|
||||
|
||||
class _RevenueLineDataState extends State<RevenueLineData> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd MMM yyyy');
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chartModel = Provider.of<ChartModel>(context);
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: mainModel.user.isOwnerAndAbove()
|
||||
? LocalText(context, 'revenue.amounts',
|
||||
color: Colors.white, fontSize: 18)
|
||||
: LocalText(context, 'spending.amounts',
|
||||
color: Colors.white, fontSize: 18),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 20),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 100,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "revenue.date")),
|
||||
MyDataColumn(label: LocalText(context, "revenue.amount"),numeric: true),
|
||||
],
|
||||
rows: getProductRow(chartModel.revenue.getData()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow(List<Data> revs) {
|
||||
return revs.map((p) {
|
||||
var r = MyDataRow(
|
||||
onSelectChanged: (bool selected) async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => RevenueLineDetail(p.date)),
|
||||
);
|
||||
},
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(dateFormatter.format(p.date), style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
NumberCell(p.amount)
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
106
lib/charts/revenue_line_detail.dart
Normal file
@@ -0,0 +1,106 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
import 'package:fcs/model/po_model.dart';
|
||||
import 'package:fcs/theme/theme.dart';
|
||||
import 'package:fcs/vo/po.dart';
|
||||
import 'package:fcs/widget/local_text.dart';
|
||||
import 'package:fcs/widget/my_data_table.dart';
|
||||
import 'package:fcs/widget/number_cell.dart';
|
||||
import 'package:fcs/widget/progress.dart';
|
||||
|
||||
class RevenueLineDetail extends StatefulWidget {
|
||||
final DateTime date;
|
||||
const RevenueLineDetail(this.date);
|
||||
@override
|
||||
_RevenueLineDetailState createState() => _RevenueLineDetailState();
|
||||
}
|
||||
|
||||
class _RevenueLineDetailState extends State<RevenueLineDetail> {
|
||||
final numberFormatter = new NumberFormat("#,###");
|
||||
var dateFormatter = new DateFormat('dd-MMM-yyyy');
|
||||
bool _isLoading = false;
|
||||
List<POSubmission> pos = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
POSubmissionModel pOModel =
|
||||
Provider.of<POSubmissionModel>(context, listen: false);
|
||||
pOModel.getPOForRevenue(widget.date).then((pos) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
this.pos = pos;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var mainModel = Provider.of<MainModel>(context);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: primaryColor,
|
||||
title: mainModel.user.isOwnerAndAbove()
|
||||
? LocalText(
|
||||
context,
|
||||
'revenue.detail.title',
|
||||
translationVariables: [dateFormatter.format(widget.date)],
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
)
|
||||
: LocalText(
|
||||
context,
|
||||
'spending.detail.title',
|
||||
translationVariables: [dateFormatter.format(widget.date)],
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
padding: EdgeInsets.only(top: 5),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: EdgeInsets.only(left: 3),
|
||||
child: MyDataTable(
|
||||
columnSpacing: 20,
|
||||
columns: [
|
||||
MyDataColumn(label: LocalText(context, "po.name")),
|
||||
MyDataColumn(label: LocalText(context, "po.po_num")),
|
||||
MyDataColumn(
|
||||
label: LocalText(context, "po.amount"), numeric: true),
|
||||
],
|
||||
rows: getProductRow(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<MyDataRow> getProductRow() {
|
||||
return pos.map((p) {
|
||||
var r = MyDataRow(
|
||||
cells: [
|
||||
MyDataCell(
|
||||
new Text(p.userName, style: textStyle),
|
||||
),
|
||||
MyDataCell(
|
||||
new Text(p.poNumber, style: textStyle),
|
||||
),
|
||||
MyDataCell(NumberCell(p.amount)),
|
||||
],
|
||||
);
|
||||
|
||||
return r;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
60
lib/charts/time.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
/// Timeseries chart example
|
||||
import 'package:charts_flutter/flutter.dart' as charts;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SimpleTimeSeriesChart extends StatelessWidget {
|
||||
final List<charts.Series> seriesList;
|
||||
final bool animate;
|
||||
|
||||
SimpleTimeSeriesChart(this.seriesList, {this.animate});
|
||||
|
||||
/// Creates a [TimeSeriesChart] with sample data and no transition.
|
||||
factory SimpleTimeSeriesChart.withSampleData() {
|
||||
return new SimpleTimeSeriesChart(
|
||||
_createSampleData(),
|
||||
// Disable animations for image tests.
|
||||
animate: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new charts.TimeSeriesChart(
|
||||
seriesList,
|
||||
animate: animate,
|
||||
// Optionally pass in a [DateTimeFactory] used by the chart. The factory
|
||||
// should create the same type of [DateTime] as the data provided. If none
|
||||
// specified, the default creates local date time.
|
||||
dateTimeFactory: const charts.LocalDateTimeFactory(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Create one series with sample hard coded data.
|
||||
static List<charts.Series<TimeSeriesSales, DateTime>> _createSampleData() {
|
||||
final data = [
|
||||
new TimeSeriesSales(new DateTime(2017, 9, 19), 5),
|
||||
new TimeSeriesSales(new DateTime(2017, 9, 26), 25),
|
||||
new TimeSeriesSales(new DateTime(2017, 10, 3), 100),
|
||||
new TimeSeriesSales(new DateTime(2017, 10, 10), 75),
|
||||
];
|
||||
|
||||
return [
|
||||
new charts.Series<TimeSeriesSales, DateTime>(
|
||||
id: 'Sales',
|
||||
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||
domainFn: (TimeSeriesSales sales, _) => sales.time,
|
||||
measureFn: (TimeSeriesSales sales, _) => sales.sales,
|
||||
data: data,
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample time series data type.
|
||||
class TimeSeriesSales {
|
||||
final DateTime time;
|
||||
final int sales;
|
||||
|
||||
TimeSeriesSales(this.time, this.sales);
|
||||
}
|
||||
48
lib/config.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
enum Flavor { DEV, STAGING, PRODUCTION, LOCAL }
|
||||
const FlavorNames = ["Development", "Staging", "Production", "Local"];
|
||||
|
||||
class Config {
|
||||
static Config _instance;
|
||||
|
||||
final Flavor flavor;
|
||||
final String name;
|
||||
final Color color;
|
||||
final String apiURL;
|
||||
final String reportURL;
|
||||
final Level level;
|
||||
final String reportProjectID;
|
||||
|
||||
factory Config(
|
||||
{@required Flavor flavor,
|
||||
@required String apiURL,
|
||||
@required String reportURL,
|
||||
@required String reportProjectID,
|
||||
Color color: Colors.blue,
|
||||
Level level: Level.SEVERE}) {
|
||||
_instance ??= Config._internal(flavor, FlavorNames[flavor.index], color,
|
||||
apiURL, reportURL, level, reportProjectID);
|
||||
|
||||
Logger.root.level = level;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
print(
|
||||
'${record.level.name}: ${record.time}: ${record.loggerName}: ${record.message}');
|
||||
});
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
Config._internal(this.flavor, this.name, this.color, this.apiURL,
|
||||
this.reportURL, this.level, this.reportProjectID);
|
||||
|
||||
static Config get instance {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static bool isProduction() => _instance.flavor == Flavor.PRODUCTION;
|
||||
static bool isDevelopment() => _instance.flavor == Flavor.DEV;
|
||||
static bool isStaging() => _instance.flavor == Flavor.STAGING;
|
||||
static bool isLocal() => _instance.flavor == Flavor.LOCAL;
|
||||
}
|
||||
177
lib/face/face_detection_camera.dart
Normal file
@@ -0,0 +1,177 @@
|
||||
import 'smile_painter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'dart:ui' as ui show Image;
|
||||
import 'utils.dart';
|
||||
|
||||
class FaceDetectionFromLiveCamera extends StatefulWidget {
|
||||
FaceDetectionFromLiveCamera({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_FaceDetectionFromLiveCameraState createState() =>
|
||||
_FaceDetectionFromLiveCameraState();
|
||||
}
|
||||
|
||||
class _FaceDetectionFromLiveCameraState
|
||||
extends State<FaceDetectionFromLiveCamera> {
|
||||
final FaceDetector faceDetector = FirebaseVision.instance.faceDetector();
|
||||
List<Face> faces;
|
||||
CameraController _camera;
|
||||
|
||||
bool _isDetecting = false;
|
||||
CameraLensDirection _direction = CameraLensDirection.back;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeCamera();
|
||||
}
|
||||
|
||||
void _initializeCamera() async {
|
||||
CameraDescription description = await getCamera(_direction);
|
||||
ImageRotation rotation = rotationIntToImageRotation(
|
||||
description.sensorOrientation,
|
||||
);
|
||||
|
||||
_camera = CameraController(
|
||||
description,
|
||||
defaultTargetPlatform == TargetPlatform.iOS
|
||||
? ResolutionPreset.low
|
||||
: ResolutionPreset.medium,
|
||||
);
|
||||
await _camera.initialize();
|
||||
|
||||
_camera.startImageStream((CameraImage image) {
|
||||
if (_isDetecting) return;
|
||||
|
||||
_isDetecting = true;
|
||||
|
||||
detect(
|
||||
image,
|
||||
FirebaseVision.instance
|
||||
.faceDetector(FaceDetectorOptions(
|
||||
mode: FaceDetectorMode.accurate,
|
||||
enableClassification: true))
|
||||
.processImage,
|
||||
rotation)
|
||||
.then(
|
||||
(dynamic result) {
|
||||
setState(() {
|
||||
faces = result;
|
||||
});
|
||||
|
||||
_isDetecting = false;
|
||||
},
|
||||
).catchError(
|
||||
(_) {
|
||||
_isDetecting = false;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildResults() {
|
||||
const Text noResultsText = const Text('No results!');
|
||||
const Text multipleFaceText = const Text('Multiple faces!');
|
||||
const Text pleaseSmileText = const Text('Please smile!');
|
||||
|
||||
if (faces == null || _camera == null || !_camera.value.isInitialized) {
|
||||
return noResultsText;
|
||||
}
|
||||
|
||||
CustomPainter painter;
|
||||
|
||||
final Size imageSize = Size(
|
||||
_camera.value.previewSize.height,
|
||||
_camera.value.previewSize.width,
|
||||
);
|
||||
|
||||
if (faces is! List<Face> ||
|
||||
faces.isEmpty ||
|
||||
faces == null ||
|
||||
faces.length == 0) return noResultsText;
|
||||
if (faces.length > 1) return multipleFaceText;
|
||||
var face = faces[0];
|
||||
if (face.smilingProbability == null || face.smilingProbability < 0.8) {
|
||||
return pleaseSmileText;
|
||||
}
|
||||
|
||||
painter = SmilePainterLiveCamera(imageSize, faces);
|
||||
return CustomPaint(
|
||||
painter: painter,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
return Container(
|
||||
constraints: const BoxConstraints.expand(),
|
||||
child: _camera == null
|
||||
? const Center(
|
||||
child: Text(
|
||||
'Initializing Camera...',
|
||||
style: TextStyle(
|
||||
color: Colors.green,
|
||||
fontSize: 30.0,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: <Widget>[
|
||||
CameraPreview(_camera),
|
||||
_buildResults(),
|
||||
Positioned(
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
height: 50.0,
|
||||
child: ListView(
|
||||
children: faces
|
||||
.map((face) => Text(
|
||||
"${face.boundingBox.center.toString()}, Smile:${face.smilingProbability}"))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _toggleCameraDirection() async {
|
||||
if (_direction == CameraLensDirection.back) {
|
||||
_direction = CameraLensDirection.front;
|
||||
} else {
|
||||
_direction = CameraLensDirection.back;
|
||||
}
|
||||
|
||||
await _camera.stopImageStream();
|
||||
await _camera.dispose();
|
||||
|
||||
setState(() {
|
||||
_camera = null;
|
||||
});
|
||||
|
||||
_initializeCamera();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Face Detection with Smile"),
|
||||
),
|
||||
body: _buildImage(),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _toggleCameraDirection,
|
||||
child: _direction == CameraLensDirection.back
|
||||
? const Icon(Icons.camera_front)
|
||||
: const Icon(Icons.camera_rear),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
59
lib/face/face_detection_image.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
import 'smile_painter.dart';
|
||||
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
|
||||
import 'dart:ui' as ui show Image;
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class FaceDetectionFromImage extends StatefulWidget {
|
||||
@override
|
||||
_FaceDetectionFromImageState createState() => _FaceDetectionFromImageState();
|
||||
}
|
||||
|
||||
class _FaceDetectionFromImageState extends State<FaceDetectionFromImage> {
|
||||
bool loading = true;
|
||||
ui.Image image;
|
||||
List<Face> faces;
|
||||
final FaceDetector faceDetector = FirebaseVision.instance.faceDetector();
|
||||
|
||||
Future<ui.Image> _loadImage(File file) async {
|
||||
final data = await file.readAsBytes();
|
||||
return await decodeImageFromList(data);
|
||||
}
|
||||
|
||||
void pickAndProcessImage() async {
|
||||
final File file = await ImagePicker.pickImage(source: ImageSource.gallery);
|
||||
final FirebaseVisionImage visionImage = FirebaseVisionImage.fromFile(file);
|
||||
faces = await faceDetector.processImage(visionImage);
|
||||
image = await _loadImage(file);
|
||||
setState(() {
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Face detection with Smile'),
|
||||
),
|
||||
body: Center(
|
||||
child: loading
|
||||
? Text('Press The floating Action Button for load image!')
|
||||
: FittedBox(
|
||||
child: SizedBox(
|
||||
width: image.width.toDouble(),
|
||||
height: image.height.toDouble(),
|
||||
child: FacePaint(
|
||||
painter: SmilePainter(image, faces),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: pickAndProcessImage,
|
||||
child: Icon(Icons.image),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
40
lib/face/home.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'face_detection_camera.dart';
|
||||
import 'face_detection_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Smile To Face App'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
RaisedButton(
|
||||
child: Text('Add Smile to Face from Image'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FaceDetectionFromImage(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
RaisedButton(
|
||||
child: Text('Add Smile to Face from Live Camera'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FaceDetectionFromLiveCamera(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
17
lib/face/main.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'home.dart';
|
||||
|
||||
void main() => runApp(MyApp());
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: HomeScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
138
lib/face/smile_painter.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'dart:ui' as ui show Image;
|
||||
import 'dart:math' as Math;
|
||||
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FacePaint extends CustomPaint {
|
||||
final CustomPainter painter;
|
||||
|
||||
FacePaint({this.painter}) : super(painter: painter);
|
||||
}
|
||||
|
||||
class SmilePainter extends CustomPainter {
|
||||
final ui.Image image;
|
||||
final List<Face> faces;
|
||||
|
||||
SmilePainter(this.image, this.faces);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (image != null) {
|
||||
canvas.drawImage(image, Offset.zero, Paint());
|
||||
}
|
||||
|
||||
final paintRectStyle = Paint()
|
||||
..color = Colors.red
|
||||
..strokeWidth = 30.0
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
//Draw Body
|
||||
final paint = Paint()..color = Colors.yellow;
|
||||
|
||||
for (var i = 0; i < faces.length; i++) {
|
||||
final radius =
|
||||
Math.min(faces[i].boundingBox.width, faces[i].boundingBox.height) / 2;
|
||||
final center = faces[i].boundingBox.center;
|
||||
final smilePaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = radius / 8;
|
||||
canvas.drawRect(faces[i].boundingBox, paintRectStyle);
|
||||
canvas.drawCircle(center, radius, paint);
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(
|
||||
center: center.translate(0, radius / 8), radius: radius / 2),
|
||||
0,
|
||||
Math.pi,
|
||||
false,
|
||||
smilePaint);
|
||||
//Draw the eyes
|
||||
canvas.drawCircle(Offset(center.dx - radius / 2, center.dy - radius / 2),
|
||||
radius / 8, Paint());
|
||||
canvas.drawCircle(Offset(center.dx + radius / 2, center.dy - radius / 2),
|
||||
radius / 8, Paint());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(SmilePainter oldDelegate) {
|
||||
return image != oldDelegate.image || faces != oldDelegate.faces;
|
||||
}
|
||||
}
|
||||
|
||||
class SmilePainterLiveCamera extends CustomPainter {
|
||||
final Size imageSize;
|
||||
final List<Face> faces;
|
||||
|
||||
SmilePainterLiveCamera(this.imageSize, this.faces);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
// final paintRectStyle = Paint()
|
||||
// ..color = Colors.red
|
||||
// ..strokeWidth = 10.0
|
||||
// ..style = PaintingStyle.stroke;
|
||||
|
||||
final paint = Paint()..color = Colors.yellow;
|
||||
|
||||
for (var i = 0; i < faces.length; i++) {
|
||||
//Scale rect to image size
|
||||
final rect = _scaleRect(
|
||||
rect: faces[i].boundingBox,
|
||||
imageSize: imageSize,
|
||||
widgetSize: size,
|
||||
);
|
||||
|
||||
//Radius for smile circle
|
||||
final radius = Math.min(rect.width, rect.height) / 2;
|
||||
|
||||
//Center of face rect
|
||||
final Offset center = rect.center;
|
||||
|
||||
final smilePaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = radius / 8;
|
||||
|
||||
//Draw rect border
|
||||
//canvas.drawRect(rect, paintRectStyle);
|
||||
|
||||
//Draw body
|
||||
canvas.drawCircle(center, radius, paint);
|
||||
|
||||
//Draw mouth
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(
|
||||
center: center.translate(0, radius / 8), radius: radius / 2),
|
||||
0,
|
||||
Math.pi,
|
||||
false,
|
||||
smilePaint);
|
||||
|
||||
//Draw the eyes
|
||||
canvas.drawCircle(Offset(center.dx - radius / 2, center.dy - radius / 2),
|
||||
radius / 8, Paint());
|
||||
canvas.drawCircle(Offset(center.dx + radius / 2, center.dy - radius / 2),
|
||||
radius / 8, Paint());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(SmilePainterLiveCamera oldDelegate) {
|
||||
return imageSize != oldDelegate.imageSize || faces != oldDelegate.faces;
|
||||
}
|
||||
}
|
||||
|
||||
Rect _scaleRect({
|
||||
@required Rect rect,
|
||||
@required Size imageSize,
|
||||
@required Size widgetSize,
|
||||
}) {
|
||||
final double scaleX = widgetSize.width / imageSize.width;
|
||||
final double scaleY = widgetSize.height / imageSize.height;
|
||||
|
||||
return Rect.fromLTRB(
|
||||
rect.left.toDouble() * scaleX,
|
||||
rect.top.toDouble() * scaleY,
|
||||
rect.right.toDouble() * scaleX,
|
||||
rect.bottom.toDouble() * scaleY,
|
||||
);
|
||||
}
|
||||
69
lib/face/utils.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
typedef HandleDetection = Future<List<Face>> Function(FirebaseVisionImage image);
|
||||
|
||||
Future<CameraDescription> getCamera(CameraLensDirection dir) async {
|
||||
return await availableCameras().then(
|
||||
(List<CameraDescription> cameras) => cameras.firstWhere(
|
||||
(CameraDescription camera) => camera.lensDirection == dir,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List concatenatePlanes(List<Plane> planes) {
|
||||
final WriteBuffer allBytes = WriteBuffer();
|
||||
planes.forEach((Plane plane) => allBytes.putUint8List(plane.bytes));
|
||||
return allBytes.done().buffer.asUint8List();
|
||||
}
|
||||
|
||||
FirebaseVisionImageMetadata buildMetaData(
|
||||
CameraImage image,
|
||||
ImageRotation rotation,
|
||||
) {
|
||||
return FirebaseVisionImageMetadata(
|
||||
rawFormat: image.format.raw,
|
||||
size: Size(image.width.toDouble(), image.height.toDouble()),
|
||||
rotation: rotation,
|
||||
planeData: image.planes.map(
|
||||
(Plane plane) {
|
||||
return FirebaseVisionImagePlaneMetadata(
|
||||
bytesPerRow: plane.bytesPerRow,
|
||||
height: plane.height,
|
||||
width: plane.width,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Face>> detect(
|
||||
CameraImage image,
|
||||
HandleDetection handleDetection,
|
||||
ImageRotation rotation,
|
||||
) async {
|
||||
return handleDetection(
|
||||
FirebaseVisionImage.fromBytes(
|
||||
concatenatePlanes(image.planes),
|
||||
buildMetaData(image, rotation),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ImageRotation rotationIntToImageRotation(int rotation) {
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
return ImageRotation.rotation0;
|
||||
case 90:
|
||||
return ImageRotation.rotation90;
|
||||
case 180:
|
||||
return ImageRotation.rotation180;
|
||||
default:
|
||||
assert(rotation == 270);
|
||||
return ImageRotation.rotation270;
|
||||
}
|
||||
}
|
||||
17
lib/main-dev.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:fcs/config.dart';
|
||||
|
||||
import 'app.dart';
|
||||
|
||||
void main() {
|
||||
Config(
|
||||
flavor: Flavor.DEV,
|
||||
color: Colors.blue,
|
||||
apiURL:
|
||||
"https://asia-northeast1-mokkon-wholesale-dev.cloudfunctions.net/APIOK",
|
||||
reportURL: "http://petrok-dev.mokkon.com:8080",
|
||||
reportProjectID: "dev",
|
||||
level: Level.ALL);
|
||||
runApp(App());
|
||||
}
|
||||
117
lib/main.dart
@@ -1,117 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// Try running your application with "flutter run". You'll see the
|
||||
// application has a blue toolbar. Then, without quitting the app, try
|
||||
// changing the primarySwatch below to Colors.green and then invoke
|
||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// is not restarted.
|
||||
primarySwatch: Colors.blue,
|
||||
// This makes the visual density adapt to the platform that you run
|
||||
// the app on. For desktop platforms, the controls will be smaller and
|
||||
// closer together (more dense) than on mobile platforms.
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
home: MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
MyHomePage({Key key, this.title}) : super(key: key);
|
||||
|
||||
// This widget is the home page of your application. It is stateful, meaning
|
||||
// that it has a State object (defined below) that contains fields that affect
|
||||
// how it looks.
|
||||
|
||||
// This class is the configuration for the state. It holds the values (in this
|
||||
// case the title) provided by the parent (in this case the App widget) and
|
||||
// used by the build method of the State. Fields in a Widget subclass are
|
||||
// always marked "final".
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
_MyHomePageState createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
// This call to setState tells the Flutter framework that something has
|
||||
// changed in this State, which causes it to rerun the build method below
|
||||
// so that the display can reflect the updated values. If we changed
|
||||
// _counter without calling setState(), then the build method would not be
|
||||
// called again, and so nothing would appear to happen.
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
// Center is a layout widget. It takes a single child and positions it
|
||||
// in the middle of the parent.
|
||||
child: Column(
|
||||
// Column is also a layout widget. It takes a list of children and
|
||||
// arranges them vertically. By default, it sizes itself to fit its
|
||||
// children horizontally, and tries to be as tall as its parent.
|
||||
//
|
||||
// Invoke "debug painting" (press "p" in the console, choose the
|
||||
// "Toggle Debug Paint" action from the Flutter Inspector in Android
|
||||
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
|
||||
// to see the wireframe for each widget.
|
||||
//
|
||||
// Column has various properties to control how it sizes itself and
|
||||
// how it positions its children. Here we use mainAxisAlignment to
|
||||
// center the children vertically; the main axis here is the vertical
|
||||
// axis because Columns are vertical (the cross axis would be
|
||||
// horizontal).
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'You have pushed the button this many times:',
|
||||
),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
}
|
||||
61
lib/model/announcement_model.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:fcs/vo/announcement.dart';
|
||||
|
||||
import 'base_model.dart';
|
||||
import 'constants.dart';
|
||||
import 'firebase_helper.dart';
|
||||
|
||||
class AnnouncementModel extends BaseModel {
|
||||
List<Announcement> announcements = [];
|
||||
|
||||
void initUser(user) {
|
||||
super.initUser(user);
|
||||
_loadAnnouncements();
|
||||
}
|
||||
|
||||
@override
|
||||
logout() async {
|
||||
announcements = [];
|
||||
}
|
||||
|
||||
Future<void> _loadAnnouncements() async {
|
||||
Stream<QuerySnapshot> snapshots = Firestore.instance
|
||||
.collection(
|
||||
"/$biz_collection/${setting.okEnergyId}/$announcement_collection")
|
||||
.snapshots();
|
||||
|
||||
snapshots.listen((snaps) async {
|
||||
announcements = snaps.documents.map((documentSnapshot) {
|
||||
var data = Announcement.fromMap(
|
||||
documentSnapshot.data, documentSnapshot.documentID);
|
||||
return data;
|
||||
}).toList();
|
||||
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Future<Announcement> getAnnouncement(String id) async {
|
||||
String path = "/$biz_collection/${setting.okEnergyId}/$announcement_collection";
|
||||
var snap = await getDocSnap(path, id);
|
||||
return Announcement.fromMap(snap.data, snap.documentID);
|
||||
}
|
||||
|
||||
Future<void> createAnnouncement(Announcement announcement) async {
|
||||
await request("/announcement", "POST",
|
||||
payload: announcement.toMap(), token: await getToken());
|
||||
}
|
||||
|
||||
Future<void> updateAnnouncement(Announcement announcement) async {
|
||||
await request("/announcement", "PUT",
|
||||
payload: announcement.toMap(), token: await getToken());
|
||||
}
|
||||
|
||||
Future<void> deleteAnnouncement(Announcement announcement) async {
|
||||
await request("/announcement", "DELETE",
|
||||
payload: announcement.toMap(), token: await getToken());
|
||||
}
|
||||
}
|
||||
147
lib/model/api_helper.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:device_info/device_info.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:fcs/vo/status.dart';
|
||||
|
||||
import '../config.dart';
|
||||
|
||||
final log = Logger('requestAPI');
|
||||
|
||||
// request makes http request
|
||||
// if token is null
|
||||
Future<dynamic> requestAPI(
|
||||
String path,
|
||||
method, {
|
||||
dynamic payload,
|
||||
String token,
|
||||
String url,
|
||||
}) async {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
String deviceName = "${androidInfo.model}(${androidInfo.id})";
|
||||
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
|
||||
|
||||
Map<String, dynamic> headers = {};
|
||||
if (token != null) {
|
||||
headers["Token"] = token;
|
||||
}
|
||||
if (androidInfo.androidId != null) {
|
||||
headers["Device"] = androidInfo.androidId + ":" + deviceName;
|
||||
}
|
||||
headers["Project-ID"] = Config.instance.reportProjectID;
|
||||
|
||||
BaseOptions options = new BaseOptions(
|
||||
method: method,
|
||||
baseUrl: url == null ? Config.instance.apiURL : url,
|
||||
connectTimeout: 10000,
|
||||
receiveTimeout: 10000,
|
||||
headers: headers,
|
||||
);
|
||||
log.info("baseUrl:${options.baseUrl}, path:$path");
|
||||
try {
|
||||
Dio dio = new Dio(options);
|
||||
Response response = await dio.request(
|
||||
path,
|
||||
data: payload,
|
||||
);
|
||||
var data = Status.fromJson(response.data);
|
||||
if (data.status == 'Ok') {
|
||||
return response.data["data"];
|
||||
} else {
|
||||
throw Exception(data.message);
|
||||
}
|
||||
} catch (e) {
|
||||
log.warning("path:$path, api:$e");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// request makes http request
|
||||
// if token is null
|
||||
Future<dynamic> requestDownloadAPI(String path, method,
|
||||
{dynamic payload, String token, String url, String filePath}) async {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
String deviceName = "${androidInfo.model}(${androidInfo.id})";
|
||||
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
|
||||
|
||||
var bytes = utf8.encode(payload);
|
||||
var base64Str = base64.encode(bytes);
|
||||
String escapePayload = HtmlEscape().convert(base64Str);
|
||||
|
||||
try {
|
||||
String baseUrl = url == null ? Config.instance.apiURL : url;
|
||||
log.info("Path:$baseUrl$path");
|
||||
HttpClient client = new HttpClient();
|
||||
var _downloadData = StringBuffer();
|
||||
var fileSave = new File(filePath);
|
||||
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
|
||||
request.headers.set("Project-ID", Config.instance.reportProjectID);
|
||||
request.headers
|
||||
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
|
||||
if (token != null) {
|
||||
request.headers.set("Token", token);
|
||||
}
|
||||
if (androidInfo.androidId != null) {
|
||||
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
|
||||
}
|
||||
request.headers.set("payload", escapePayload);
|
||||
var response = await request.close();
|
||||
print("headers:${response.headers}");
|
||||
response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
|
||||
onDone: () {
|
||||
fileSave.writeAsString(_downloadData.toString());
|
||||
});
|
||||
} catch (e) {
|
||||
log.warning("path:$path, api:$e");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// request makes http request
|
||||
// if token is null
|
||||
Future<dynamic> requestDownloadPDFAPI(String path, method,
|
||||
{dynamic payload, String token, String url, String filePath}) async {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||
String deviceName = "${androidInfo.model}(${androidInfo.id})";
|
||||
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
|
||||
|
||||
var bytes = utf8.encode(payload);
|
||||
var base64Str = base64.encode(bytes);
|
||||
String escapePayload = HtmlEscape().convert(base64Str);
|
||||
|
||||
try {
|
||||
String baseUrl = url == null ? Config.instance.apiURL : url;
|
||||
log.info("Path:$baseUrl$path");
|
||||
HttpClient client = new HttpClient();
|
||||
// var _downloadData = StringBuffer();
|
||||
var fileSave = new File(filePath);
|
||||
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
|
||||
request.headers.set("Project-ID", Config.instance.reportProjectID);
|
||||
if (token != null) {
|
||||
request.headers.set("Token", token);
|
||||
}
|
||||
if (androidInfo.androidId != null) {
|
||||
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
|
||||
}
|
||||
request.headers.set("payload", escapePayload);
|
||||
var response = await request.close();
|
||||
print("headers:${response.headers}");
|
||||
var _downloadData = List<int>();
|
||||
|
||||
response.listen((d) => _downloadData.addAll(d), onDone: () {
|
||||
fileSave.writeAsBytes(_downloadData);
|
||||
});
|
||||
// response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
|
||||
// onDone: () {
|
||||
// fileSave.writeAsString(_downloadData.toString());
|
||||
// });
|
||||
} catch (e) {
|
||||
log.warning("path:$path, api:$e");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
36
lib/model/base_model.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:fcs/model/api_helper.dart';
|
||||
import 'package:fcs/model/main_model.dart';
|
||||
|
||||
import '../vo/setting.dart';
|
||||
import '../vo/user.dart';
|
||||
|
||||
abstract class BaseModel extends ChangeNotifier {
|
||||
User user;
|
||||
Setting setting;
|
||||
MainModel mainModel;
|
||||
|
||||
void initUser(User user) async {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
void initSetting(Setting setting) async {
|
||||
this.setting = setting;
|
||||
}
|
||||
|
||||
void logout();
|
||||
|
||||
// request makes http request
|
||||
// if token is null
|
||||
dynamic request(
|
||||
String path,
|
||||
method, {
|
||||
dynamic payload,
|
||||
String token,
|
||||
String url,
|
||||
}) async {
|
||||
mainModel.resetPinTimer();
|
||||
return await requestAPI(path, method,
|
||||
payload: payload, token: token, url: url);
|
||||
}
|
||||
}
|
||||