add structure

This commit is contained in:
2020-05-29 07:45:27 +06:30
parent 4c851d9971
commit bad27ba5c4
272 changed files with 36065 additions and 174 deletions

11
.vscode/launch.json vendored
View File

@@ -2,9 +2,14 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Flutter", "name": "Flutter Dev",
"request": "launch", "request": "launch",
"type": "dart" "type": "dart",
} "program": "lib/main-dev.dart",
"args": [
"-t",
"lib/main-dev.dart",
],
},
] ]
} }

View File

@@ -34,7 +34,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.mokkon.fcs_dev.fcs" applicationId "com.mokkon.fcs_dev.fcs"
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

Binary file not shown.

BIN
assets/address.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
assets/admin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
assets/amount.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
assets/approve.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
assets/block.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/buyer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/date_filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets/device.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/do.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
assets/email.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/employee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
assets/eng_flag.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/gender.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
assets/img/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/img/login_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/inventory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
assets/licence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View 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"
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/logo1 (copy).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/logo_title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
assets/manual.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
assets/message.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

0
assets/my_file.json Normal file
View File

BIN
assets/myan_flag.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
assets/page.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
assets/password.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

BIN
assets/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
assets/pdo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/phone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
assets/pin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
assets/product.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
assets/product_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
assets/quota.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/r.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
assets/reg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/report.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
assets/sales.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
assets/status.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
assets/storage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/storage12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/submission.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/term.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
assets/truck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
assets/volume.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/whole.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

163
lib/app.dart Normal file
View 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
View 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 ?? [];
});
}
}

View 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],
))),
),
),
],
),
);
}
}

View 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();
}
}

View 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,
),
),
),
),
],
),
);
}
}

View 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();
}
}

View 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,
// ),
// ),
// ),
// ),
],
),
);
}
}

View 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();
}
}

View 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();
}
}

View 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,
),
),
),
),
],
),
);
}
}

View 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
View 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],
))),
),
),
],
),
);
}
}

View 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
View 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);
}

View 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 ?? [];
});
}
}
}

View 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();
}
}

View 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
View 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],
))),
),
),
],
),
);
}
}

View 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();
}
}

View 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
View 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);
}

View 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],
))),
),
),
],
),
);
}
}

View 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();
}
}

View 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
View 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
View 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;
}

View 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),
),
);
}
}

View 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
View 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
View 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
View 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
View 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
View 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());
}

View File

@@ -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.
);
}
}

View 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
View 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
View 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);
}
}

Some files were not shown because too many files have changed in this diff Show More