v5.6.0 - update

This commit is contained in:
Anthony 2022-01-03 17:58:52 +00:00
parent d7a38cb95e
commit 5c4addcc66
116 changed files with 3522 additions and 1465 deletions

View File

@ -15,7 +15,7 @@ DEFAULT_LOCALE=null
# *<! ------ WooSignal Config ------!>*
APP_KEY="your app key"
APP_KEY="app_affb6434339b34443a297c2e40a3edab7102137e6d67de9abfe612b749bd"
# App key from WooSignal link: https://woosignal.com/dashboard/apps
# *<! ------ STRIPE (OPTIONAL) ------!>*

View File

@ -1,3 +1,15 @@
## [5.6.0] - 2022-01-03
* Fix bug with banner in Mello theme
* Support new languages - Dutch (nl) and Turkish (tr)
* Refactor as per dart analysis
* Ability to add coupons
* Wishlist
* New theme "Compo"
* Analysis options added
* Code cleanup
* Pubspec.yaml dependency updates
## [5.5.2] - 2021-12-18
* Fix continuous loading on categories screen

View File

@ -1,6 +1,6 @@
BSD 2-Clause License
Copyright (c) 2021, WooSignal Ltd
Copyright (c) 2022, WooSignal Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -4,7 +4,7 @@
# WooCommerce App: Label StoreMax
### Label StoreMax - v5.5.2
### Label StoreMax - v5.6.0
[Official WooSignal WooCommerce App](https://woosignal.com)

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Bitte geben Sie eine gültige Versand-E-Mail ein",
"Free postage": "Portofrei",
"PROCESSING": "WIRD BEARBEITET",
"Social": "Sozial"
"Social": "Sozial",
"Please enter coupon to redeem": "Bitte Coupon zum Einlösen eingeben",
"Coupon": "Coupon",
"Apply": "Sich bewerben",
"Apply Coupon": "Gutschein anwenden",
"Added to checkout": "Zur Kasse hinzugefügt",
"Redeem Coupon": "Gutschein einlösen",
"Add coupon code": "Gutscheincode hinzufügen",
"Coupon not found": "Gutschein nicht gefunden",
"Sorry, this coupon can not be used with your cart": "Dieser Gutschein kann leider nicht mit Ihrem Warenkorb verwendet werden",
"You cannot redeem this coupon": "Sie können diesen Gutschein nicht einlösen",
"This coupon has expired": "Dieser Gutschein ist abgelaufen",
"Usage limit has been reached": "Nutzungslimit wurde erreicht",
"View All": "Alle ansehen",
"Wishlist": "Wunschzettel",
"No items found": "Keine Elemente gefunden",
"This product has been removed from your wishlist": "Dieses Produkt wurde von Ihrer Wunschliste entfernt",
"This product has been added to your wishlist": "Dieses Produkt wurde Ihrer Wunschliste hinzugefügt",
"Spend a minimum of minimumAmount to redeem": "Verbringen Sie mindestens {{minimumAmount}} einlösen",
"Spend less than maximumAmount to redeem": "Geben Sie weniger aus als {{maximumAmount}} einlösen"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Please enter a valid shipping email",
"Free postage": "Free postage",
"PROCESSING": "PROCESSING",
"Social": "Social"
"Social": "Social",
"Please enter coupon to redeem": "Please enter coupon to redeem",
"Coupon": "Coupon",
"Apply": "Apply",
"Apply Coupon": "Apply Coupon",
"Spend a minimum of minimumAmount to redeem": "Spend a minimum of {{minimumAmount}} to redeem",
"Spend less than maximumAmount to redeem": "Spend less than {{maximumAmount}} to redeem",
"Added to checkout": "Added to checkout",
"Redeem Coupon": "Redeem Coupon",
"Add coupon code": "Add coupon code",
"Coupon not found": "Coupon not found",
"Sorry, this coupon can not be used with your cart": "Sorry, this coupon can not be used with your cart",
"You cannot redeem this coupon": "You cannot redeem this coupon",
"This coupon has expired": "This coupon has expired",
"Usage limit has been reached": "Usage limit has been reached",
"View All": "View All",
"Wishlist": "Wishlist",
"No items found": "No items found",
"This product has been removed from your wishlist": "This product has been removed from your wishlist",
"This product has been added to your wishlist": "This product has been added to your wishlist"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Ingrese un correo electrónico de envío válido",
"Free postage": "Gastos de envío gratis",
"PROCESSING": "PROCESANDO",
"Social": "Social"
"Social": "Social",
"Please enter coupon to redeem": "Ingrese el cupón para canjear",
"Coupon": "Cupón",
"Apply": "Solicitar",
"Apply Coupon": "Aplicar cupón",
"Added to checkout": "Agregado al pago",
"Redeem Coupon": "Canjear cupón",
"Add coupon code": "Agregar código de cupón",
"Coupon not found": "Cupón no encontrado",
"Sorry, this coupon can not be used with your cart": "Lo sentimos, este cupón no se puede utilizar con su carrito",
"You cannot redeem this coupon": "No puedes canjear este cupón",
"This coupon has expired": "Este cupón ha caducado",
"Usage limit has been reached": "Se alcanzó el límite de uso",
"View All": "Ver todo",
"Wishlist": "Lista de deseos",
"No items found": "No se encontraron artículos",
"This product has been removed from your wishlist": "Este producto ha sido eliminado de tu lista de deseos.",
"This product has been added to your wishlist": "Este producto ha sido añadido a su lista de deseos",
"Spend a minimum of minimumAmount to redeem": "Gasta un mínimo de {{minimumAmount}} para redimir",
"Spend less than maximumAmount to redeem": "Gasta menos de {{maximumAmount}} para redimir"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Veuillez saisir un e-mail de livraison valide",
"Free postage": "Sans frais de port",
"PROCESSING": "EN TRAITEMENT",
"Social": "Social"
"Social": "Social",
"Please enter coupon to redeem": "Veuillez entrer le coupon pour l'utiliser",
"Coupon": "Coupon",
"Apply": "Appliquer",
"Apply Coupon": "Appliquer Coupon",
"Added to checkout": "Ajouté à la caisse",
"Redeem Coupon": "Échanger le coupon",
"Add coupon code": "Ajouter un code promo",
"Coupon not found": "Coupon introuvable",
"Sorry, this coupon can not be used with your cart": "Désolé, ce coupon ne peut pas être utilisé avec votre panier",
"You cannot redeem this coupon": "Vous ne pouvez pas utiliser ce coupon",
"This coupon has expired": "Ce coupon a expiré",
"Usage limit has been reached": "La limite d'utilisation a été atteinte",
"View All": "Voir tout",
"Wishlist": "Liste de souhaits",
"No items found": "Aucun élément trouvé",
"This product has been removed from your wishlist": "Ce produit a été supprimé de votre liste de souhaits",
"This product has been added to your wishlist": "Ce produit a été ajouté à votre liste de souhaits",
"Spend a minimum of minimumAmount to redeem": "Dépensez un minimum de {{minimumAmount}} de racheter",
"Spend less than maximumAmount to redeem": "Dépensez moins de {{maximumAmount}} de racheter"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "krpaya ek maany shiping eemel darj karen",
"Free postage": "mupht daak",
"PROCESSING": "prasanskaran",
"Social": "saamaajik"
"Social": "saamaajik",
"Please enter coupon to redeem": "rideem karane ke lie krpaya koopan darj karen",
"Coupon": "koopan",
"Apply": "laagoo karana",
"Apply Coupon": "koopan laagoo karen",
"Added to checkout": "chekaut mein joda gaya",
"Redeem Coupon": "koopan rideem karaen",
"Add coupon code": "koopan kod joden",
"Coupon not found": "koopan nahin mila",
"Sorry, this coupon can not be used with your cart": "kshama karen, is koopan ka upayog aapake kaart ke saath nahin kiya ja sakata",
"You cannot redeem this coupon": "aap is koopan ko rideem nahin kar sakate",
"This coupon has expired": "yah koopan samaapt ho gaya hai",
"Usage limit has been reached": "upayog kee seema pooree ho chukee hai",
"View All": "sabhee ko dekhen",
"Wishlist": "ichchha-soochee",
"No items found": "kuchh nahin mila",
"This product has been removed from your wishlist": "yah utpaad aapakee ichchha soochee se hata diya gaya hai",
"This product has been added to your wishlist": "is utpaad ko aapakee vish - list mein jod diya gaya hai",
"Spend a minimum of minimumAmount to redeem": "kam se kam kharch karen {{minimumAmount}} ke evaj mein lena",
"Spend less than maximumAmount to redeem": "se kam kharch karen {{maximumAmount}} ke evaj mein lena"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Si prega di inserire un'e-mail di spedizione valida",
"Free postage": "Spedizione gratuita",
"PROCESSING": "IN LAVORAZIONE",
"Social": "Sociale"
"Social": "Sociale",
"Please enter coupon to redeem": "Inserisci il coupon per riscattare",
"Coupon": "Buono",
"Apply": "Applicare",
"Apply Coupon": "Applicare il coupon",
"Added to checkout": "Aggiunto al checkout",
"Redeem Coupon": "Utilizza il coupon",
"Add coupon code": "Aggiungi il codice coupon",
"Coupon not found": "Coupon non trovato",
"Sorry, this coupon can not be used with your cart": "Spiacenti, questo coupon non può essere utilizzato con il carrello",
"You cannot redeem this coupon": "Non puoi riscattare questo coupon",
"This coupon has expired": "Questo coupon è scaduto",
"Usage limit has been reached": "È stato raggiunto il limite di utilizzo",
"View All": "Mostra tutto",
"Wishlist": "Lista dei desideri",
"No items found": "Nessun articolo trovato",
"This product has been removed from your wishlist": "Questo prodotto è stato rimosso dalla tua lista dei desideri",
"This product has been added to your wishlist": "Questo prodotto è stato aggiunto alla tua lista dei desideri",
"Spend a minimum of minimumAmount to redeem": "Spendi un minimo di {{minimumAmount}} riscattare",
"Spend less than maximumAmount to redeem": "Spendi meno di {{maximumAmount}} riscattare"
}

214
LabelStoreMax/lang/nl.json Normal file
View File

@ -0,0 +1,214 @@
{
"Categories": "Categorieën",
"Shop": "Winkel",
"Newest": "Nieuwste",
"Browse categories": "Bladeren door rubrieken",
"Cart": "winkelwagentje",
"You need items in your cart to checkout": "Je hebt artikelen in je winkelwagen nodig om af te rekenen",
"Updated": "Bijgewerkt",
"Item removed": "Artikel verwijderd",
"Success": "succes",
"Cart cleared": "winkelwagen leeggemaakt",
"Shopping Cart": "Winkelwagen",
"Clear Cart": "Winkelwagen leegmaken",
"Empty Basket": "Lege mand",
"PROCEED TO CHECKOUT": "GA NAAR DE KASSA",
"Browse": "Bladeren",
"Search results for": "zoekresultaten voor",
"Select a": "Selecteer een",
"Added to cart": "Toegevoegd aan winkelwagen",
"Options": "Opties",
"Price": "prijs",
"Choose your options": "Kies je opties",
"Out of stock": "Uitverkocht",
"In Stock": "op voorraad",
"Add to cart": "Voeg toe aan winkelmandje",
"Oops": "oeps",
"Please select valid options first": "Selecteer eerst geldige opties",
"Sorry": "Sorry",
"This item is not in stock": "Dit artikel is niet op voorraad",
"Description": "Beschrijving",
"Full description": "Volledige beschrijving",
"ADD TO CART": "VOEG TOE AAN WINKELMANDJE",
"This item is out of stock": "Dit product is niet meer op voorraad",
"Add your shipping details first": "Voeg eerst uw verzendgegevens toe",
"Checkout": "uitchecken",
"Billing/shipping details": "Factuur-/verzendgegevens",
"Add billing & shipping details": "Facturerings- en verzendgegevens toevoegen",
"Payment method": "Betalingsmiddel",
"Pay with": "betaal met",
"Select a payment method": "Kies een betalingsmethode",
"Shipping selected": "Verzending geselecteerd",
"Select shipping": "Selecteer verzending",
"Select a shipping option": "Selecteer een verzendoptie",
"Shipping fee": "Verzendkosten",
"Subtotal": "subtotaal",
"Total": "Totaal",
"CHECKOUT": "UITCHECKEN",
"One moment": "een moment",
"Please select add your billing/shipping address to proceed": "Selecteer uw factuur-/verzendadres toevoegen om door te gaan",
"Your billing/shipping details are incomplete": "Uw factuur-/verzendgegevens zijn onvolledig",
"Please select a shipping method to proceed": "Selecteer een verzendmethode om verder te gaan",
"Please select a payment method to proceed": "Selecteer een betaalmethode om door te gaan",
"Something went wrong, please contact our store": "Er is iets misgegaan, neem contact op met onze winkel!",
"Error": "Fout",
"Order Status": "Bestelstatus",
"Thank You!": "Dank u!",
"Your transaction details": "Uw transactiegegevens",
"Order Ref": "Bestelreferentie",
"Items": "Artikelen",
"Back to Home": "Terug naar huis",
"Orders": "bestellingen",
"Billing & Shipping Details": "Facturerings- en verzendgegevens",
"First Name": "Voornaam",
"Last Name": "Achternaam",
"Address Line": "Adresregel",
"City": "Stad",
"Postal code": "Postcode",
"Email address": "E-mailadres",
"Selected": "Geselecteerd",
"Select country": "Selecteer land",
"Remember my details": "Onthoud mijn gegevens",
"USE SHIPPING ADDRESS": "VERZENDADRES GEBRUIKEN",
"About": "Over",
"Privacy policy": "Privacybeleid",
"Terms and conditions": "Voorwaarden",
"Version": "versie",
"Payment Method": "Betalingsmiddel",
"CANCEL": "ANNULEREN",
"Shipping Methods": "Verzendmethoden:",
"Shipping is not supported for your country, sorry": "Verzending wordt niet ondersteund voor uw land, sorry",
"Search": "Zoekopdracht",
"Debit or Credit Card": "Debet- of creditcard",
"Oops, something went wrong": "Oeps! Er is iets misgegaan",
"Tax": "Belasting",
"No results": "Geen resultaten",
"There is an item out of stock": "Er is een artikel niet op voorraad",
"Maximum stock reached": "Maximale voorraad bereikt",
"Select a country": "Selecteer een land",
"Menu": "Menu",
"About Us": "Over ons",
"Something went wrong": "Er is iets fout gegaan",
"Product variation does not exist": "Productvariatie bestaat niet",
"This variation is unavailable": "Deze variant is niet beschikbaar",
"Sorry, something went wrong": "Sorry, er ging iets mis",
"Back": "Rug",
"Profile": "Profiel",
"Forgot Password": "Wachtwoord vergeten",
"Create an account": "Account aanmaken",
"Login": "Log in",
"Password": "Wachtwoord",
"Oops!": "Oeps!",
"Invalid login credentials": "Ongeldige inloggegevens",
"That email address is not valid": "Dat e-mailadres is niet geldig",
"Password must be a min 6 characters": "Wachtwoord moet minimaal 6 tekens lang zijn",
"Please check your details": "Kijk alsjeblieft je gegevens na",
"Invalid": "Invalide",
"Actions": "Acties",
"View Terms and Conditions or Privacy policy": "Bekijk de algemene voorwaarden of het privacybeleid",
"Terms and Conditions": "Voorwaarden",
"Privacy Policy": "Privacybeleid",
"terms and conditions": "voorwaarden",
"and": "en",
"By tapping \"Register\" you agree to ": "Door op \"Registreren\" te tikken, gaat u akkoord met:",
"privacy policy": "privacybeleid",
"Sign up": "Inschrijven",
"Email": "E-mail",
"Update details": "Gegevens bijwerken",
"Settings": "Instellingen",
"Account": "Rekening",
"Logout": "Uitloggen",
"No orders found": "Geen bestellingen gevonden",
"items": "artikelen",
"Update Details": "Gegevens bijwerken",
"Invalid details": "Ongeldige gegevens",
"Please check your email and password": "Controleer uw e-mail en wachtwoord",
"Something went wrong, please try again.": "Er is iets mis gegaan, probeer het alstublieft nogmaals.",
"Done": "Gedaan",
"Billing Details": "Factureringsgegevens",
"Shipping Details": "Verzendgegevens",
"Shipping Address": "Verzendingsadres",
"State": "Staat",
"Country": "Land",
"UPDATE DETAILS": "DETAILS BIJWERKEN",
"No more products": "Geen producten meer",
"release to load more": "laat los om meer te laden",
"Load Failed! Click retry!": "Laden mislukt! Klik opnieuw!",
"pull up load": "trek de lading omhoog",
"Sort: Low to high": "Sorteren: laag naar hoog",
"Sort: High to low": "Sorteren: hoog naar laag",
"Sort: Name A-Z": "Sorteren: Naam A-Z",
"Sort: Name Z-A": "Sorteren: Naam Z-A",
"Cancel": "Annuleren",
"Sort results": "Sorteer resultaten",
"you're now logged in": "Je bent nu ingelogd",
"Hello": "Hallo",
"Welcome back": "Welkom terug",
"Quantity": "Hoeveelheid",
"Select a state": "Selecteer een staat",
"Select state": "Selecteer staat",
"Ship to a different address?": "Verzend naar een ander adres?",
"USE DETAILS": "GEBRUIK DETAILS",
"Not supported, try a card payment": "Niet ondersteund, probeer een kaartbetaling",
"Invalid shipping address, please check your shipping details": "Ongeldig verzendadres, controleer uw verzendgegevens",
"Was": "Was",
"off": "uit",
"Maximum quantity reached": "Maximale hoeveelheid bereikt",
"Sorry, only": "Sorry, alleen",
"left": "links",
"Billing address is incomplete": "Factuuradres is onvolledig",
"Order": "Volgorde",
"Date Ordered": "Datum besteld",
"Ships to": "wordt verzonden naar",
"That email does not match our records": "Dat e-mailadres komt niet overeen met onze gegevens",
"That username does not match our records": "Die gebruikersnaam komt niet overeen met onze gegevens",
"That password does not match our records": "Dat wachtwoord komt niet overeen met onze gegevens",
"The email and password field cannot be empty": "Het veld voor e-mail en wachtwoord mag niet leeg zijn",
"Username taken, try another.": "Die gebruikersnaam is in gebruik, probeer een andere.",
"A user already exists": "Er bestaat al een gebruiker",
"That email is taken, try another": "Die e-mail is in gebruik, probeer een andere",
"The email field is empty": "Het e-mailveld is leeg",
"No more orders": "Geen bestellingen meer",
"Account updated": "Account bijgewerkt",
"Spend a minimum of": "Besteed minimaal €",
"for": "voor",
"Buy Product": "Koop product",
"Retry": "Opnieuw proberen",
"Retry later": "Probeer het later opnieuw",
"Light Mode": "Lichtmodus",
"Dark Mode": "Donkere modus",
"PayPal Checkout": "PayPal Afrekenen",
"Processing Payment": "Betaling verwerken",
"Please wait, your order is being processed and you will be redirected to the PayPal website.": "Een ogenblik geduld, uw bestelling wordt verwerkt en u wordt doorgestuurd naar de PayPal-website.",
"If you are not automatically redirected to PayPal within 5 seconds": "Als u niet binnen 5 seconden automatisch wordt doorgestuurd naar PayPal",
"Payment Cancelled": "Betaling geannuleerd",
"The payment has been cancelled": "De betaling is geannuleerd",
"Must have": "Hebbeding",
"Our selection of new items": "Onze selectie van nieuwe items",
"Register": "Register",
"No payment methods are available": "Er zijn geen betaalmethoden beschikbaar",
"Please enter a valid billing email": "Voer een geldig e-mailadres voor facturering in",
"Please enter a valid shipping email": "Voer een geldig verzend-e-mailadres in",
"Free postage": "Gratis verzending",
"PROCESSING": "VERWERKEN",
"Social": "sociaal",
"Please enter coupon to redeem": "Voer de coupon in om deze in te wisselen",
"Coupon": "Coupon",
"Apply": "Van toepassing zijn",
"Apply Coupon": "gebruik coupon",
"Added to checkout": "Toegevoegd aan afrekenen",
"Redeem Coupon": "coupon inwisselen",
"Add coupon code": "Voeg couponcode toe",
"Coupon not found": "Coupon niet gevonden",
"Sorry, this coupon can not be used with your cart": "Sorry, deze coupon kan niet worden gebruikt met uw winkelwagentje",
"You cannot redeem this coupon": "U kunt deze coupon niet inwisselen",
"This coupon has expired": "Deze coupon is verlopen",
"Usage limit has been reached": "Gebruikslimiet is bereikt",
"View All": "Bekijk alles",
"Wishlist": "verlanglijst",
"No items found": "Geen items gevonden",
"This product has been removed from your wishlist": "Dit product is van je verlanglijst verwijderd",
"This product has been added to your wishlist": "Dit product is toegevoegd aan je verlanglijst",
"Spend a minimum of minimumAmount to redeem": "Besteed minimaal € {{minimumAmount}} verlossen",
"Spend less than maximumAmount to redeem": "Minder uitgeven dan {{maximumAmount}} verlossen"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "Por favor, insira um e-mail de envio válido",
"Free postage": "Postagem grátis",
"PROCESSING": "EM PROCESSAMENTO",
"Social": "Social"
"Social": "Social",
"Please enter coupon to redeem": "Por favor, insira o cupom para resgatar",
"Coupon": "Cupom",
"Apply": "Aplicar",
"Apply Coupon": "Aplicar cupom",
"Added to checkout": "Adicionado ao checkout",
"Redeem Coupon": "Resgatar cupom",
"Add coupon code": "Adicionar código de cupom",
"Coupon not found": "Cupom não encontrado",
"Sorry, this coupon can not be used with your cart": "Desculpe, este cupom não pode ser usado com seu carrinho",
"You cannot redeem this coupon": "Você não pode resgatar este cupom",
"This coupon has expired": "Este cupom expirou",
"Usage limit has been reached": "O limite de uso foi atingido",
"View All": "Ver tudo",
"Wishlist": "Lista de Desejos",
"No items found": "Nenhum item encontrado",
"This product has been removed from your wishlist": "Este produto foi removido da sua lista de desejos",
"This product has been added to your wishlist": "Este produto foi adicionado à sua lista de desejos",
"Spend a minimum of minimumAmount to redeem": "Gaste um mínimo de {{minimumAmount}} redimir",
"Spend less than maximumAmount to redeem": "Gaste menos que {{maximumAmount}} redimir"
}

214
LabelStoreMax/lang/tr.json Normal file
View File

@ -0,0 +1,214 @@
{
"Categories": "Kategoriler",
"Shop": "Mağaza",
"Newest": "en yeni",
"Browse categories": "Kategorilere Gözat",
"Cart": "Araba",
"You need items in your cart to checkout": "Ödeme yapmak için sepetinizdeki öğelere ihtiyacınız var",
"Updated": "Güncellenmiş",
"Item removed": "Öğe kaldırıldı",
"Success": "Başarı",
"Cart cleared": "Sepet temizlendi",
"Shopping Cart": "Alışveriş Sepeti",
"Clear Cart": "Sepeti Temizle",
"Empty Basket": "Boş sepet",
"PROCEED TO CHECKOUT": "ÇIKIŞA DOĞRU DEVAM ET",
"Browse": "Araştır",
"Search results for": "için arama sonuçları",
"Select a": "Bir seçin",
"Added to cart": "Sepete eklendi",
"Options": "Seçenekler",
"Price": "Fiyat",
"Choose your options": "Seçeneklerinizi seçin",
"Out of stock": "Stoklar tükendi",
"In Stock": "Stokta var",
"Add to cart": "Sepete ekle",
"Oops": "ayy",
"Please select valid options first": "Lütfen önce geçerli seçenekleri seçin",
"Sorry": "Üzgünüm",
"This item is not in stock": "Bu ürün stokta yok",
"Description": "Açıklama",
"Full description": "Tam tanım",
"ADD TO CART": "SEPETE EKLE",
"This item is out of stock": "Bu ürün stokta yok",
"Add your shipping details first": "Önce gönderim bilgilerinizi ekleyin",
"Checkout": "Ödeme",
"Billing/shipping details": "Fatura/gönderim ayrıntıları",
"Add billing & shipping details": "Fatura ve gönderim ayrıntılarını ekleyin",
"Payment method": "Ödeme şekli",
"Pay with": "İle ödemek",
"Select a payment method": "Bir ödeme şekli seçin",
"Shipping selected": "Sevkiyat seçildi",
"Select shipping": "Gönderim seç",
"Select a shipping option": "Bir nakliye seçeneği seçin",
"Shipping fee": "Nakliye ücreti",
"Subtotal": "ara toplam",
"Total": "Toplam",
"CHECKOUT": "ÖDEME",
"One moment": "Bir dakika",
"Please select add your billing/shipping address to proceed": "Devam etmek için lütfen fatura/gönderim adresinizi ekleyin'i seçin",
"Your billing/shipping details are incomplete": "Faturalandırma/gönderim bilgileriniz eksik",
"Please select a shipping method to proceed": "Lütfen devam etmek için bir gönderim yöntemi seçin",
"Please select a payment method to proceed": "Lütfen devam etmek için bir ödeme yöntemi seçin",
"Something went wrong, please contact our store": "Bir şeyler ters gitti, lütfen mağazamızla iletişime geçin",
"Error": "Hata",
"Order Status": "Sipariş durumu",
"Thank You!": "Teşekkürler!",
"Your transaction details": "İşlem ayrıntılarınız",
"Order Ref": "Sipariş Referansı",
"Items": "Öğeler",
"Back to Home": "Eve geri dön",
"Orders": "Emirler",
"Billing & Shipping Details": "Faturalandırma ve Gönderim Ayrıntıları",
"First Name": "İlk adı",
"Last Name": "Soyadı",
"Address Line": "Adres satırı",
"City": "Şehir",
"Postal code": "Posta kodu",
"Email address": "E",
"Selected": "Seçildi",
"Select country": "Ülke seçin",
"Remember my details": "Ayrıntılarımı hatırla",
"USE SHIPPING ADDRESS": "NAKLİYE ADRESİNİ KULLAN",
"About": "Hakkında",
"Privacy policy": "Gizlilik Politikası",
"Terms and conditions": "Şartlar ve koşullar",
"Version": "sürüm",
"Payment Method": "Ödeme şekli",
"CANCEL": "İPTAL ET",
"Shipping Methods": "Nakliye Yöntemleri",
"Shipping is not supported for your country, sorry": "Ülkeniz için gönderim desteklenmiyor, üzgünüm",
"Search": "Aramak",
"Debit or Credit Card": "Banka veya Kredi Kartı",
"Oops, something went wrong": "Hoop! Birşeyler yanlış gitti",
"Tax": "Vergi",
"No results": "Sonuç yok",
"There is an item out of stock": "Stokta olmayan bir ürün var",
"Maximum stock reached": "Maksimum stoka ulaşıldı",
"Select a country": "Bir ülke seçin",
"Menu": "Menü",
"About Us": "Hakkımızda",
"Something went wrong": "Bir şeyler yanlış gitti",
"Product variation does not exist": "Ürün varyasyonu mevcut değil",
"This variation is unavailable": "Bu varyasyon kullanılamıyor",
"Sorry, something went wrong": "Üzgünüm, bir şeyler ters gitti",
"Back": "Geri",
"Profile": "Profil",
"Forgot Password": "Parolanızı mı unuttunuz",
"Create an account": "Bir hesap oluşturun",
"Login": "Giriş yapmak",
"Password": "Parola",
"Oops!": "Hata!",
"Invalid login credentials": "Geçersiz giriş bilgileri",
"That email address is not valid": "Bu e-posta adresi geçerli değil",
"Password must be a min 6 characters": "Şifre en az 6 karakter olmalıdır",
"Please check your details": "Lütfen bilgilerinizi kontrol edin",
"Invalid": "Geçersiz",
"Actions": "Hareketler",
"View Terms and Conditions or Privacy policy": "Hüküm ve Koşulları veya Gizlilik Politikasını Görüntüle",
"Terms and Conditions": "Şartlar ve koşullar",
"Privacy Policy": "Gizlilik Politikası",
"terms and conditions": "Şartlar ve koşullar",
"and": "ve",
"By tapping \"Register\" you agree to ": "\"Kaydol\"a dokunarak şunları kabul etmiş olursunuz:",
"privacy policy": "Gizlilik Politikası",
"Sign up": "Üye olmak",
"Email": "E-posta",
"Update details": "Ayrıntıları güncelle",
"Settings": "Ayarlar",
"Account": "Hesap",
"Logout": ıkış Yap",
"No orders found": "sipariş bulunamadı",
"items": "öğeler",
"Update Details": "Ayrıntıları Güncelle",
"Invalid details": "Geçersiz ayrıntılar",
"Please check your email and password": "Lütfen e-posta adresinizi ve şifrenizi kontrol edin",
"Something went wrong, please try again.": "Bir şeyler ters gitti lütfen tekrar deneyin.",
"Done": "Tamamlandı",
"Billing Details": "Fatura Detayları",
"Shipping Details": "Nakliye ayrıntıları",
"Shipping Address": "Teslimat Adresi",
"State": "Belirtmek, bildirmek",
"Country": "Ülke",
"UPDATE DETAILS": "GÜNCELLEME DETAYLARI",
"No more products": "Daha fazla ürün yok",
"release to load more": "daha fazla yüklemek için bırakın",
"Load Failed! Click retry!": "Yükleme başarısız! Yeniden dene'yi tıklayın!",
"pull up load": "yükü yukarı çekmek",
"Sort: Low to high": "Sıralama: Düşükten yükseğe",
"Sort: High to low": "Sıralama: Yüksekten düşüğe",
"Sort: Name A-Z": "Sırala: İsim A-Z",
"Sort: Name Z-A": "Sıralama: İsim Z-A",
"Cancel": "İptal etmek",
"Sort results": "Sonuçları sırala",
"you're now logged in": "şimdi giriş yaptınız",
"Hello": "Merhaba",
"Welcome back": "Tekrar hoşgeldiniz",
"Quantity": "Miktar",
"Select a state": "Bir eyalet seçin",
"Select state": "Eyalet seç",
"Ship to a different address?": "Farklı bir adrese mi gönderiyorsunuz?",
"USE DETAILS": "KULLANIM DETAYLARI",
"Not supported, try a card payment": "Desteklenmiyor, kartla ödeme yapmayı deneyin",
"Invalid shipping address, please check your shipping details": "Geçersiz gönderim adresi, lütfen gönderim ayrıntılarınızı kontrol edin",
"Was": "Oldu",
"off": "kapalı",
"Maximum quantity reached": "Maksimum miktara ulaşıldı",
"Sorry, only": "Üzgünüm, sadece",
"left": "ayrıldı",
"Billing address is incomplete": "Fatura adresi eksik",
"Order": "Emir",
"Date Ordered": "Sipariş Tarihi",
"Ships to": "Şuraya gönderilir:",
"That email does not match our records": "Bu e-posta kayıtlarımızla eşleşmiyor",
"That username does not match our records": "Bu kullanıcı adı kayıtlarımızla eşleşmiyor",
"That password does not match our records": "Bu şifre kayıtlarımızla eşleşmiyor",
"The email and password field cannot be empty": "E-posta ve şifre alanı boş olamaz",
"Username taken, try another.": "Bu kullanıcı adı alınmış, başka bir tane deneyin.",
"A user already exists": "Bir kullanıcı zaten var",
"That email is taken, try another": "Bu e-posta alındı, başka bir tane deneyin",
"The email field is empty": "E-posta alanı boş",
"No more orders": "Başka sipariş yok",
"Account updated": "Hesap güncellendi",
"Spend a minimum of": "Minimum harcamak",
"for": "için",
"Buy Product": "Ürün Satın Al",
"Retry": "yeniden dene",
"Retry later": "daha sonra tekrar dene",
"Light Mode": "Işık Modu",
"Dark Mode": "Karanlık Mod",
"PayPal Checkout": "PayPal Ödemesi",
"Processing Payment": "Ödeme İşlemi",
"Please wait, your order is being processed and you will be redirected to the PayPal website.": "Lütfen bekleyin, siparişiniz işleniyor ve PayPal web sitesine yönlendirileceksiniz.",
"If you are not automatically redirected to PayPal within 5 seconds": "5 saniye içinde otomatik olarak PayPal'a yönlendirilmezseniz",
"Payment Cancelled": "Ödeme İptal Edildi",
"The payment has been cancelled": "ödeme iptal edildi",
"Must have": "sahip olmalı",
"Our selection of new items": "Yeni ürün seçimimiz",
"Register": "Kayıt olmak",
"No payment methods are available": "Ödeme yöntemi yok",
"Please enter a valid billing email": "Lütfen geçerli bir fatura e-postası girin",
"Please enter a valid shipping email": "Lütfen geçerli bir gönderim e-postası girin",
"Free postage": "Bedava posta",
"PROCESSING": "İŞLEME",
"Social": "Sosyal",
"Please enter coupon to redeem": "Lütfen kullanmak için kupon girin",
"Coupon": "Kupon",
"Apply": "Uygulamak",
"Apply Coupon": "kuponu onayla",
"Added to checkout": "ödemeye eklendi",
"Redeem Coupon": "Kuponu Kullan",
"Add coupon code": "Kupon kodu ekle",
"Coupon not found": "Kupon bulunamadı",
"Sorry, this coupon can not be used with your cart": "Üzgünüz, bu kupon sepetinizle kullanılamaz",
"You cannot redeem this coupon": "Bu kuponu kullanamazsınız",
"This coupon has expired": "Bu kuponun süresi doldu",
"Usage limit has been reached": "Kullanım sınırına ulaşıldı",
"View All": "Hepsini gör",
"Wishlist": "istek listesi",
"No items found": "hiç bir öğe bulunamadı",
"This product has been removed from your wishlist": "Bu ürün istek listenizden kaldırıldı",
"This product has been added to your wishlist": "Bu ürün istek listenize eklendi",
"Spend a minimum of minimumAmount to redeem": "Minimum harcamak {{minimumAmount}} rehinden kurtarmak",
"Spend less than maximumAmount to redeem": "Şundan daha az harcayın: {{maximumAmount}} rehinden kurtarmak"
}

View File

@ -191,5 +191,24 @@
"Please enter a valid shipping email": "请输入有效的送货电子邮件",
"Free postage": "免邮费",
"PROCESSING": "加工",
"Social": "社会的"
"Social": "社会的",
"Please enter coupon to redeem": "请输入优惠券进行兑换",
"Coupon": "优惠券",
"Apply": "申请",
"Apply Coupon": "申请优惠券",
"Added to checkout": "添加到结帐",
"Redeem Coupon": "兑换优惠券",
"Add coupon code": "添加优惠券代码",
"Coupon not found": "未找到优惠券",
"Sorry, this coupon can not be used with your cart": "抱歉,此优惠券不能与您的购物车一起使用",
"You cannot redeem this coupon": "您无法兑换此优惠券",
"This coupon has expired": "此优惠券已过期",
"Usage limit has been reached": "已达到使用限制",
"View All": "查看全部",
"Wishlist": "愿望清单",
"No items found": "未找到任何项目",
"This product has been removed from your wishlist": "该产品已从您的愿望清单中删除",
"This product has been added to your wishlist": "本产品已经被加入你的心愿单",
"Spend a minimum of minimumAmount to redeem": "至少花费 {{minimumAmount}} 赎回",
"Spend less than maximumAmount to redeem": "花费少于 {{maximumAmount}} 赎回"
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,23 +1,17 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:nylo_support/controllers/controller.dart';
/// Base Controller for the Nylo
/// See more on controllers here - https://nylo.dev/docs/2.x/controllers
class Controller extends BaseController {
Controller();
@override
construct(BuildContext context) {
super.construct(context);
}
}

View File

@ -0,0 +1,29 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/app/controllers/woosignal_api_loader_controller.dart';
import 'package:woosignal/models/response/order.dart';
class CustomerOrdersLoaderController
extends WooSignalApiLoaderController<Order> {
CustomerOrdersLoaderController();
Future<void> loadOrders(
{@required bool Function(bool hasProducts) hasResults,
@required void Function() didFinish,
@required String userId}) async {
await load(
hasResults: hasResults,
didFinish: didFinish,
apiQuery: (api) => api.getOrders(
customer: int.parse(userId), page: page, perPage: 50));
}
}

View File

@ -0,0 +1,34 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/app/controllers/woosignal_api_loader_controller.dart';
import 'package:woosignal/models/response/product_category.dart';
import 'package:woosignal/models/response/products.dart';
class ProductCategorySearchLoaderController
extends WooSignalApiLoaderController<Product> {
ProductCategorySearchLoaderController();
Future<void> loadProducts(
{@required bool Function(bool hasProducts) hasResults,
@required void Function() didFinish,
@required ProductCategory productCategory}) async {
await load(
hasResults: hasResults,
didFinish: didFinish,
apiQuery: (api) => api.getProducts(
perPage: 50,
category: productCategory.id.toString(),
page: page,
status: "publish",
stockStatus: "instock"));
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -0,0 +1,32 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/app/controllers/woosignal_api_loader_controller.dart';
import 'package:woosignal/models/response/products.dart';
class ProductLoaderController extends WooSignalApiLoaderController<Product> {
ProductLoaderController();
Future<void> loadProducts({
@required bool Function(bool hasProducts) hasResults,
@required void Function() didFinish,
}) async {
await load(
hasResults: hasResults,
didFinish: didFinish,
apiQuery: (api) => api.getProducts(
perPage: 50,
page: page,
status: "publish",
stockStatus: "instock",
));
}
}

View File

@ -0,0 +1,34 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/app/controllers/woosignal_api_loader_controller.dart';
import 'package:woosignal/models/response/products.dart';
class ProductSearchLoaderController
extends WooSignalApiLoaderController<Product> {
ProductSearchLoaderController();
Future<void> loadProducts(
{@required bool Function(bool hasProducts) hasResults,
@required void Function() didFinish,
@required String search}) async {
await load(
hasResults: hasResults,
didFinish: didFinish,
apiQuery: (api) => api.getProducts(
perPage: 100,
search: search,
page: page,
status: "publish",
stockStatus: "instock",
));
}
}

View File

@ -0,0 +1,53 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:woosignal/woosignal.dart';
class WooSignalApiLoaderController<T> {
List<T> _results = [];
int page = 1;
bool _waitForNextRequest = false;
WooSignalApiLoaderController();
Future<void> load(
{@required bool Function(bool hasProducts) hasResults,
@required void Function() didFinish,
@required Future<List<T>> Function(WooSignal query) apiQuery}) async {
if (_waitForNextRequest) {
return;
}
_waitForNextRequest = true;
List<T> apiResults = await appWooSignal((api) => apiQuery(api));
if (!hasResults(apiResults.isNotEmpty)) {
return;
}
_results.addAll(apiResults);
page = page + 1;
_waitForNextRequest = false;
didFinish();
}
List<T> getResults() {
return _results;
}
void clear() {
_results = [];
_waitForNextRequest = false;
page = 1;
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -0,0 +1,19 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
class BottomNavItem {
int id;
BottomNavigationBarItem bottomNavigationBarItem;
Widget tabWidget;
BottomNavItem({this.id, this.bottomNavigationBarItem, this.tabWidget});
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -70,9 +70,15 @@ class Cart {
Future<String> getTotal({bool withFormat = false}) async {
List<CartLineItem> cartLineItems = await getCart();
double total = 0;
cartLineItems.forEach((cartItem) {
for (var cartItem in cartLineItems) {
total += (parseWcPrice(cartItem.total) * cartItem.quantity);
});
}
CheckoutSession checkoutSession = CheckoutSession.getInstance;
if (checkoutSession.coupon != null) {
String discountAmount = await Cart.getInstance.couponDiscountAmount();
total = total - double.parse(discountAmount);
}
if (withFormat != null && withFormat == true) {
return formatDoubleCurrency(total: total);
@ -83,9 +89,9 @@ class Cart {
Future<String> getSubtotal({bool withFormat = false}) async {
List<CartLineItem> cartLineItems = await getCart();
double subtotal = 0;
cartLineItems.forEach((cartItem) {
for (var cartItem in cartLineItems) {
subtotal += (parseWcPrice(cartItem.subtotal) * cartItem.quantity);
});
}
if (withFormat != null && withFormat == true) {
return formatDoubleCurrency(total: subtotal);
}
@ -97,7 +103,7 @@ class Cart {
@required int incrementQuantity}) async {
List<CartLineItem> cartLineItems = await getCart();
List<CartLineItem> tmpCartItem = [];
cartLineItems.forEach((cartItem) {
for (var cartItem in cartLineItems) {
if (cartItem.variationId == cartLineItem.variationId &&
cartItem.productId == cartLineItem.productId) {
if ((cartItem.quantity + incrementQuantity) > 0) {
@ -105,7 +111,7 @@ class Cart {
}
}
tmpCartItem.add(cartItem);
});
}
await saveCartToPref(cartLineItems: tmpCartItem);
}
@ -145,10 +151,14 @@ class Cart {
double cartSubtotal = 0;
if (AppHelper.instance.appConfig.productPricesIncludeTax == 1 &&
taxableCartLines.length > 0) {
taxableCartLines.isNotEmpty) {
cartSubtotal = taxableCartLines
.map<double>((m) => parseWcPrice(m.subtotal) * m.quantity)
.reduce((a, b) => a + b);
if (CheckoutSession.getInstance.coupon != null) {
String discountAmount = await Cart.getInstance.couponDiscountAmount();
cartSubtotal = cartSubtotal - double.parse(discountAmount);
}
}
subtotal = cartSubtotal;
@ -189,4 +199,86 @@ class Cart {
}
return (total).toStringAsFixed(2);
}
Future<String> couponDiscountAmount() async {
CheckoutSession checkoutSession = CheckoutSession.getInstance;
if (checkoutSession.coupon == null) {
return "0";
}
List<CartLineItem> cartLineItems = await getCart();
List<CartLineItem> eligibleCartLineItems = [];
double subtotal = 0;
for (var cartItem in cartLineItems) {
bool canContinue = true;
if (checkoutSession.coupon.excludedProductCategories.isNotEmpty) {
for (var excludedProductCategory
in checkoutSession.coupon.excludedProductCategories) {
if (cartItem.categories
.map((category) => category.id)
.contains(excludedProductCategory)) {
canContinue = false;
break;
}
}
}
if (checkoutSession.coupon.productCategories.isNotEmpty) {
for (var productCategories
in checkoutSession.coupon.productCategories) {
if (cartItem.categories
.map((category) => category.id)
.contains(productCategories) ==
false) {
canContinue = false;
break;
}
}
}
if (canContinue == false) {
continue;
}
if (checkoutSession.coupon.excludeSaleItems == true &&
cartItem.onSale == true) {
continue;
}
if (checkoutSession.coupon.excludedProductIds.isNotEmpty &&
checkoutSession.coupon.excludedProductIds
.contains(cartItem.productId)) {
continue;
}
if (checkoutSession.coupon.productIds.isNotEmpty &&
!checkoutSession.coupon.productIds.contains(cartItem.productId)) {
continue;
}
subtotal += (parseWcPrice(cartItem.subtotal) * cartItem.quantity);
eligibleCartLineItems.add(cartItem);
}
String discountType = checkoutSession.coupon.discountType;
String amount = checkoutSession.coupon.amount;
// Percentage
if (discountType == 'percent') {
return ((subtotal * double.parse(amount)) / 100).toStringAsFixed(2);
}
// Fixed cart
if (discountType == 'fixed_cart') {
return (double.parse(amount)).toStringAsFixed(2);
}
// Fixed product
if (discountType == 'fixed_product') {
return (eligibleCartLineItems.length * double.parse(amount))
.toStringAsFixed(2);
}
return "0";
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -9,6 +9,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/product_variation.dart';
import 'package:woosignal/models/response/products.dart' as ws_product;
class CartLineItem {
String name;
@ -25,6 +28,8 @@ class CartLineItem {
String total;
String imageSrc;
String variationOptions;
List<ws_product.Category> categories;
bool onSale;
String stockStatus;
Object metaData = {};
@ -39,10 +44,12 @@ class CartLineItem {
this.shippingClassId,
this.taxStatus,
this.taxClass,
this.categories,
this.shippingIsTaxable,
this.variationOptions,
this.imageSrc,
this.subtotal,
this.onSale,
this.total,
this.metaData});
@ -50,6 +57,53 @@ class CartLineItem {
return (quantity * parseWcPrice(subtotal)).toStringAsFixed(2);
}
CartLineItem.fromProduct({int quantityAmount, ws_product.Product product}) {
name = product.name;
productId = product.id;
quantity = quantityAmount;
taxStatus = product.taxStatus;
shippingClassId = product.shippingClassId.toString();
subtotal = product.price;
taxClass = product.taxClass;
categories = product.categories;
isManagedStock = product.manageStock;
stockQuantity = product.stockQuantity;
shippingIsTaxable = product.shippingTaxable;
imageSrc = product.images.isEmpty
? getEnv("PRODUCT_PLACEHOLDER_IMAGE")
: product.images.first.src;
total = product.price;
}
CartLineItem.fromProductVariation(
{int quantityAmount,
List<String> options,
ws_product.Product product,
ProductVariation productVariation}) {
String imageSrc = getEnv("PRODUCT_PLACEHOLDER_IMAGE");
if (product.images.isNotEmpty) {
imageSrc = product.images.first.src;
}
if (productVariation.image != null) {
imageSrc = productVariation.image.src;
}
name = product.name;
productId = product.id;
variationId = productVariation.id;
quantity = quantityAmount;
taxStatus = productVariation.taxStatus;
shippingClassId = productVariation.shippingClassId.toString();
subtotal = productVariation.price;
stockQuantity = productVariation.stockQuantity;
isManagedStock = productVariation.manageStock;
categories = product.categories;
taxClass = productVariation.taxClass;
this.imageSrc = imageSrc;
shippingIsTaxable = product.shippingTaxable;
variationOptions = options.join("; ");
total = productVariation.price;
}
CartLineItem.fromJson(Map<String, dynamic> json)
: name = json['name'],
productId = json['product_id'],
@ -68,6 +122,12 @@ class CartLineItem {
taxClass = json['tax_class'],
stockStatus = json['stock_status'],
imageSrc = json['image_src'],
categories = json['categories'] == null
? null
: List.of(json['categories'] as List)
.map((e) => ws_product.Category.fromJson(e))
.toList(),
onSale = json['on_sale'],
variationOptions = json['variation_options'],
metaData = json['metaData'];
@ -84,8 +144,12 @@ class CartLineItem {
'stock_quantity': stockQuantity,
'shipping_is_taxable': shippingIsTaxable,
'image_src': imageSrc,
'categories': categories != null
? categories.map((e) => e.toJson()).toList()
: [],
'variation_options': variationOptions,
'subtotal': subtotal,
'on_sale': onSale,
'total': total,
'meta_data': metaData,
};

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -17,6 +17,7 @@ import 'package:flutter_app/app/models/shipping_type.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/bootstrap/shared_pref/shared_key.dart';
import 'package:nylo_support/helpers/helper.dart';
import 'package:woosignal/models/response/coupon.dart';
import 'package:woosignal/models/response/tax_rate.dart';
class CheckoutSession {
@ -29,6 +30,7 @@ class CheckoutSession {
BillingDetails billingDetails;
ShippingType shippingType;
PaymentType paymentType;
Coupon coupon;
void initSession() {
billingDetails = BillingDetails();
@ -39,6 +41,7 @@ class CheckoutSession {
billingDetails = null;
shippingType = null;
paymentType = null;
coupon = null;
}
void saveBillingAddress() async {

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -39,13 +39,13 @@ class CustomerAddress {
}
bool hasMissingFields() =>
(this.firstName.isEmpty ||
this.lastName.isEmpty ||
this.addressLine.isEmpty ||
this.city.isEmpty ||
this.postalCode.isEmpty) ||
(this.customerCountry.hasState() == true
? (this.customerCountry?.state?.name ?? "").isEmpty
(firstName.isEmpty ||
lastName.isEmpty ||
addressLine.isEmpty ||
city.isEmpty ||
postalCode.isEmpty) ||
(customerCountry.hasState() == true
? (customerCountry?.state?.name ?? "").isEmpty
: false);
String addressFull() {
@ -59,12 +59,11 @@ class CustomerAddress {
if (postalCode != null && postalCode != "") {
tmpArrAddress.add(postalCode);
}
if (this.customerCountry != null &&
this.customerCountry?.state?.name != null) {
tmpArrAddress.add(this.customerCountry?.state?.name);
if (customerCountry != null && customerCountry?.state?.name != null) {
tmpArrAddress.add(customerCountry?.state?.name);
}
if (this.customerCountry != null && this.customerCountry?.name != null) {
tmpArrAddress.add(this.customerCountry.name);
if (customerCountry != null && customerCountry?.name != null) {
tmpArrAddress.add(customerCountry.name);
}
return tmpArrAddress.join(", ");
}
@ -86,23 +85,23 @@ class CustomerAddress {
addressLine = json['address_line'];
city = json['city'];
postalCode = json['postal_code'];
this.customerCountry = CustomerCountry.fromJson(json['customer_country']);
customerCountry = CustomerCountry.fromJson(json['customer_country']);
emailAddress = json['email_address'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['first_name'] = this.firstName;
data['last_name'] = this.lastName;
data['address_line'] = this.addressLine;
data['city'] = this.city;
data['postal_code'] = this.postalCode;
data['state'] = this.customerCountry.state;
data['country'] = this.customerCountry.name;
data['email_address'] = this.emailAddress;
final Map<String, dynamic> data = <String, dynamic>{};
data['first_name'] = firstName;
data['last_name'] = lastName;
data['address_line'] = addressLine;
data['city'] = city;
data['postal_code'] = postalCode;
data['state'] = customerCountry.state;
data['country'] = customerCountry.name;
data['email_address'] = emailAddress;
data['customer_country'] = null;
if (this.customerCountry != null) {
data['customer_country'] = this.customerCountry.toJson();
if (customerCountry != null) {
data['customer_country'] = customerCountry.toJson();
}
return data;
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -40,16 +40,15 @@ class CustomerCountry {
}
}
bool hasState() =>
(this.state != null && this.state.name != null ? true : false);
bool hasState() => (state != null && state.name != null ? true : false);
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['country_code'] = this.countryCode;
data['name'] = this.name;
final Map<String, dynamic> data = <String, dynamic>{};
data['country_code'] = countryCode;
data['name'] = name;
data['state'] = null;
if (this.state != null) {
data['state'] = this.state.toJson();
if (state != null) {
data['state'] = state.toJson();
}
return data;
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -25,14 +25,14 @@ class DefaultShippingState {
DefaultShippingState({@required this.code, @required this.name});
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
final Map<String, dynamic> data = <String, dynamic>{};
data['code'] = code;
data['name'] = name;
return data;
}
DefaultShippingState.fromJson(Map<String, dynamic> json) {
this.code = json['code'];
this.name = json['name'];
code = json['code'];
name = json['name'];
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -32,20 +32,20 @@ class ShippingType {
};
String getTotal({bool withFormatting = false}) {
if (this.object != null) {
switch (this.methodId) {
if (object != null) {
switch (methodId) {
case "flat_rate":
FlatRate flatRate = (this.object as FlatRate);
FlatRate flatRate = (object as FlatRate);
return (withFormatting == true
? formatStringCurrency(total: cost)
: flatRate.cost);
case "free_shipping":
FreeShipping freeShipping = (this.object as FreeShipping);
FreeShipping freeShipping = (object as FreeShipping);
return (withFormatting == true
? formatStringCurrency(total: cost)
: freeShipping.cost);
case "local_pickup":
LocalPickup localPickup = (this.object as LocalPickup);
LocalPickup localPickup = (object as LocalPickup);
return (withFormatting == true
? formatStringCurrency(total: cost)
: localPickup.cost);
@ -57,16 +57,16 @@ class ShippingType {
}
String getTitle() {
if (this.object != null) {
switch (this.methodId) {
if (object != null) {
switch (methodId) {
case "flat_rate":
FlatRate flatRate = (this.object as FlatRate);
FlatRate flatRate = (object as FlatRate);
return flatRate.title;
case "free_shipping":
FreeShipping freeShipping = (this.object as FreeShipping);
FreeShipping freeShipping = (object as FreeShipping);
return freeShipping.title;
case "local_pickup":
LocalPickup localPickup = (this.object as LocalPickup);
LocalPickup localPickup = (object as LocalPickup);
return localPickup.title;
default:
return "";
@ -76,27 +76,27 @@ class ShippingType {
}
Map<String, dynamic> toShippingLineFee() {
if (this.object != null) {
if (object != null) {
Map<String, dynamic> tmpShippingLinesObj = {};
switch (this.methodId) {
switch (methodId) {
case "flat_rate":
FlatRate flatRate = (this.object as FlatRate);
FlatRate flatRate = (object as FlatRate);
tmpShippingLinesObj["method_title"] = flatRate.title;
tmpShippingLinesObj["method_id"] = flatRate.methodId;
tmpShippingLinesObj["total"] = this.cost;
tmpShippingLinesObj["total"] = cost;
break;
case "free_shipping":
FreeShipping freeShipping = (this.object as FreeShipping);
FreeShipping freeShipping = (object as FreeShipping);
tmpShippingLinesObj["method_title"] = freeShipping.title;
tmpShippingLinesObj["method_id"] = freeShipping.methodId;
tmpShippingLinesObj["total"] = this.cost;
tmpShippingLinesObj["total"] = cost;
break;
case "local_pickup":
LocalPickup localPickup = (this.object as LocalPickup);
LocalPickup localPickup = (object as LocalPickup);
tmpShippingLinesObj["method_title"] = localPickup.title;
tmpShippingLinesObj["method_id"] = localPickup.methodId;
tmpShippingLinesObj["total"] = this.cost;
tmpShippingLinesObj["total"] = cost;
break;
default:
return null;

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -18,11 +18,11 @@ class User extends Storable {
User.fromUserAuthResponse({this.userId, this.token});
@override
toStorage() => {"token": this.token, "user_id": this.userId};
toStorage() => {"token": token, "user_id": userId};
@override
fromStorage(dynamic data) {
this.token = data['token'];
this.userId = data['user_id'];
token = data['token'];
userId = data['user_id'];
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -3,7 +3,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -3,7 +3,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -3,7 +3,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -39,38 +39,36 @@ payPalPay(context,
showToastNotification(
context,
title: trans("Payment Cancelled"),
description:
trans("The payment has been cancelled"),
description: trans("The payment has been cancelled"),
);
state.reloadState(showLoader: false);
return;
}
state.reloadState(showLoader: true);
if (value.containsKey("status") && value["status"] == "success") {
OrderWC orderWC =
await buildOrderWC(taxRate: taxRate, markPaid: true);
Order order = await appWooSignal((api) => api.createOrder(orderWC));
state.reloadState(showLoader: true);
if (value.containsKey("status") && value["status"] == "success") {
OrderWC orderWC = await buildOrderWC(taxRate: taxRate, markPaid: true);
Order order = await appWooSignal((api) => api.createOrder(orderWC));
if (order == null) {
showToastNotification(
context,
title: trans("Error"),
description: trans("Something went wrong, please contact our store"),
);
return;
}
Navigator.pushNamed(context, "/checkout-status", arguments: order);
return;
} else {
if (order == null) {
showToastNotification(
context,
title: trans("Payment Cancelled"),
description: trans("The payment has been cancelled"),
title: trans("Error"),
description:
trans("Something went wrong, please contact our store"),
);
return;
}
Navigator.pushNamed(context, "/checkout-status", arguments: order);
return;
} else {
showToastNotification(
context,
title: trans("Payment Cancelled"),
description: trans("The payment has been cancelled"),
);
}
);
});
state.reloadState(showLoader: false);
});
}

View File

@ -3,7 +3,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -32,13 +32,17 @@ stripePay(context,
: getEnv('STRIPE_LIVE_MODE', defaultValue: false);
// CONFIGURE STRIPE
Stripe.stripeAccountId = getEnv('STRIPE_ACCOUNT') == null ? wooSignalApp.stripeAccount : getEnv('STRIPE_ACCOUNT');
Stripe.stripeAccountId =
getEnv('STRIPE_ACCOUNT') ?? wooSignalApp.stripeAccount;
Stripe.publishableKey = liveMode ? "pk_live_IyS4Vt86L49jITSfaUShumzi" : "pk_test_0jMmpBntJ6UkizPkfiB8ZJxH"; // Don't change this value
Stripe.publishableKey = liveMode
? "pk_live_IyS4Vt86L49jITSfaUShumzi"
: "pk_test_0jMmpBntJ6UkizPkfiB8ZJxH"; // Don't change this value
await Stripe.instance.applySettings();
if (Stripe.stripeAccountId == '') {
NyLogger.error('You need to connect your Stripe account to WooSignal via the dashboard https://woosignal.com/dashboard');
NyLogger.error(
'You need to connect your Stripe account to WooSignal via the dashboard https://woosignal.com/dashboard');
return;
}
@ -51,14 +55,12 @@ stripePay(context,
"line1": billingDetails.shippingAddress.addressLine,
"city": billingDetails.shippingAddress.city,
"postal_code": billingDetails.shippingAddress.postalCode,
"country":
(billingDetails.shippingAddress?.customerCountry?.name ?? "")
"country": (billingDetails.shippingAddress?.customerCountry?.name ?? "")
};
String cartShortDesc = await cart.cartShortDesc();
rsp = await appWooSignal((api) =>
api.stripePaymentIntent(
rsp = await appWooSignal((api) => api.stripePaymentIntent(
amount: total,
email: billingDetails.billingAddress.emailAddress,
desc: cartShortDesc,
@ -69,21 +71,25 @@ stripePay(context,
if (rsp == null) {
showToastNotification(context,
title: trans("Oops!"),
description:
trans("Something went wrong, please try again."),
description: trans("Something went wrong, please try again."),
icon: Icons.payment,
style: ToastNotificationStyleType.WARNING);
state.reloadState(showLoader: false);
return;
}
await Stripe.instance.initPaymentSheet(paymentSheetParameters: SetupPaymentSheetParameters(
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
applePay: false,
googlePay: false,
style: Theme.of(state.context).brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark,
style: Theme.of(state.context).brightness == Brightness.light
? ThemeMode.light
: ThemeMode.dark,
testEnv: liveMode,
merchantCountryCode: envVal('STRIPE_COUNTRY_CODE', defaultValue: wooSignalApp.stripeCountryCode),
merchantDisplayName: envVal('APP_NAME', defaultValue: wooSignalApp.appName),
merchantCountryCode: envVal('STRIPE_COUNTRY_CODE',
defaultValue: wooSignalApp.stripeCountryCode),
merchantDisplayName:
envVal('APP_NAME', defaultValue: wooSignalApp.appName),
paymentIntentClientSecret: rsp['client_secret'],
));
@ -105,8 +111,7 @@ stripePay(context,
}
Navigator.pushNamed(context, "/checkout-status", arguments: order);
} on StripeException catch(e) {
} on StripeException catch (e) {
if (getEnv('APP_DEBUG', defaultValue: true)) {
NyLogger.error(e.error.message);
}

View File

@ -62,11 +62,17 @@ class AppBuild extends StatelessWidget {
Widget build(BuildContext context) {
return LocalizedApp(
child: ThemeProvider(
themes: appThemes.map((appTheme) => appTheme.toAppTheme(defaultTheme: appTheme.theme.brightness == Brightness.light ? lightTheme : darkTheme)).toList(),
themes: appThemes
.map((appTheme) => appTheme.toAppTheme(
defaultTheme: appTheme.theme.brightness == Brightness.light
? lightTheme
: darkTheme))
.toList(),
child: ThemeConsumer(
child: Builder(
builder: (themeContext) => ValueListenableBuilder(
valueListenable: ValueNotifier(locale ?? NyLocalization.instance.locale),
valueListenable:
ValueNotifier(locale ?? NyLocalization.instance.locale),
builder: (context, Locale locale, _) => MaterialApp(
navigatorKey: navigatorKey,
themeMode: themeMode,
@ -86,8 +92,8 @@ class AppBuild extends StatelessWidget {
actions: actions,
title: title ?? "",
initialRoute: initialRoute,
onGenerateRoute: this.onGenerateRoute,
darkTheme: this.darkTheme == null ? ThemeConfig.dark().theme : this.darkTheme,
onGenerateRoute: onGenerateRoute,
darkTheme: darkTheme ?? ThemeConfig.dark().theme,
theme: themeData ?? ThemeProvider.themeOf(context).data,
localeResolutionCallback:
(Locale locale, Iterable<Locale> supportedLocales) {
@ -95,7 +101,8 @@ class AppBuild extends StatelessWidget {
},
localizationsDelegates: NyLocalization.instance.delegates,
locale: NyLocalization.instance.locale,
supportedLocales: supportedLocales ?? NyLocalization.instance.locals(),
supportedLocales:
supportedLocales ?? NyLocalization.instance.locals(),
),
),
),

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -9,11 +9,16 @@ class BaseThemeConfig {
final BaseColorStyles colors;
final dynamic meta;
BaseThemeConfig({this.id, this.description, this.theme, this.colors, this.meta = const {}});
BaseThemeConfig(
{this.id,
this.description,
this.theme,
this.colors,
this.meta = const {}});
AppTheme toAppTheme({ThemeData defaultTheme}) => AppTheme(
id: this.id,
data: defaultTheme == null ? this.theme : defaultTheme,
description: this.description,
);
}
id: id,
data: defaultTheme ?? theme,
description: description,
);
}

View File

@ -12,12 +12,12 @@ import 'package:wp_json_api/wp_json_api.dart';
/// boot application
Future<void> boot() async {
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
await WooSignal.instance.init(appKey: getEnv('APP_KEY'), debugMode: getEnv('APP_DEBUG'));
await WooSignal.instance
.init(appKey: getEnv('APP_KEY'), debugMode: getEnv('APP_DEBUG'));
// Notifications
/// await Firebase.initializeApp(
@ -78,7 +78,8 @@ Future<void> boot() async {
);
}
if (getEnv('DEFAULT_LOCALE', defaultValue: null) == null && wooSignalApp.locale != null) {
if (getEnv('DEFAULT_LOCALE', defaultValue: null) == null &&
wooSignalApp.locale != null) {
locale = Locale(wooSignalApp.locale);
} else {
locale = Locale(envVal('DEFAULT_LOCALE', defaultValue: 'en'));
@ -91,6 +92,5 @@ Future<void> boot() async {
languageCode: locale.languageCode,
languagesList: languagesList,
assetsDirectory: assetsDirectory,
valuesAsMap: valuesAsMap
);
}
valuesAsMap: valuesAsMap);
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -21,10 +21,11 @@ import 'package:woosignal/models/response/tax_rate.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
Future<OrderWC> buildOrderWC({TaxRate taxRate, bool markPaid = true}) async {
CheckoutSession checkoutSession = CheckoutSession.getInstance;
OrderWC orderWC = OrderWC();
WooSignalApp wooSignalApp = AppHelper.instance.appConfig;
String paymentMethodName = CheckoutSession.getInstance.paymentType.name ?? "";
String paymentMethodName = checkoutSession.paymentType.name ?? "";
orderWC.paymentMethod = Platform.isAndroid
? "$paymentMethodName - Android App"
@ -40,7 +41,7 @@ Future<OrderWC> buildOrderWC({TaxRate taxRate, bool markPaid = true}) async {
List<LineItems> lineItems = [];
List<CartLineItem> cartItems = await Cart.getInstance.getCart();
cartItems.forEach((cartItem) {
for (var cartItem in cartItems) {
LineItems tmpLineItem = LineItems();
tmpLineItem.quantity = cartItem.quantity;
tmpLineItem.name = cartItem.name;
@ -51,11 +52,11 @@ Future<OrderWC> buildOrderWC({TaxRate taxRate, bool markPaid = true}) async {
tmpLineItem.subtotal = cartItem.subtotal;
lineItems.add(tmpLineItem);
});
}
orderWC.lineItems = lineItems;
BillingDetails billingDetails = CheckoutSession.getInstance.billingDetails;
BillingDetails billingDetails = checkoutSession.billingDetails;
Billing billing = Billing();
billing.firstName = billingDetails.billingAddress.firstName;
@ -88,7 +89,7 @@ Future<OrderWC> buildOrderWC({TaxRate taxRate, bool markPaid = true}) async {
orderWC.shippingLines = [];
if (wooSignalApp.disableShipping != 1) {
Map<String, dynamic> shippingLineFeeObj =
CheckoutSession.getInstance.shippingType.toShippingLineFee();
checkoutSession.shippingType.toShippingLineFee();
if (shippingLineFeeObj != null) {
ShippingLines shippingLine = ShippingLines();
shippingLine.methodId = shippingLineFeeObj['method_id'];
@ -108,5 +109,11 @@ Future<OrderWC> buildOrderWC({TaxRate taxRate, bool markPaid = true}) async {
orderWC.feeLines.add(feeLines);
}
if (checkoutSession.coupon != null) {
orderWC.couponLines = [];
CouponLines couponLine = CouponLines(code: checkoutSession.coupon.code);
orderWC.couponLines.add(couponLine);
}
return orderWC;
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -9,8 +9,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
enum SortByType {
LowToHigh,
HighToLow,
NameAZ,
NameZA,
lowToHigh,
highToLow,
nameAZ,
nameZA,
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -44,53 +44,70 @@ import 'package:woosignal/woosignal.dart';
Future<User> getUser() async =>
(await NyStorage.read<User>(SharedKey.authUser, model: User()));
appWooSignal(Function(WooSignal) api) async {
Future appWooSignal(Function(WooSignal) api) async {
return await api(WooSignal.instance);
}
/// helper to find correct color from the [context].
class ThemeColor {
static BaseColorStyles get(BuildContext context) {
return ((Theme.of(context).brightness == Brightness.light) ? ThemeConfig.light().colors : ThemeConfig.dark().colors);
return ((Theme.of(context).brightness == Brightness.light)
? ThemeConfig.light().colors
: ThemeConfig.dark().colors);
}
}
/// helper to set colors on TextStyle
extension ColorsHelper on TextStyle {
TextStyle setColor(BuildContext context, Color Function(BaseColorStyles color) newColor) {
return this.copyWith(color: newColor(ThemeColor.get(context)));
TextStyle setColor(
BuildContext context, Color Function(BaseColorStyles color) newColor) {
return copyWith(color: newColor(ThemeColor.get(context)));
}
}
List<PaymentType> getPaymentTypes() {
List<PaymentType> paymentTypes = [];
app_payment_gateways.forEach((element) {
if (paymentTypes.firstWhere((paymentType) => paymentType.name != element, orElse: () => null) == null) {
paymentTypes.add(paymentTypeList.firstWhere((paymentTypeList) => paymentTypeList.name == element, orElse: () => null));
for (var appPaymentGateway in app_payment_gateways) {
if (paymentTypes.firstWhere(
(paymentType) => paymentType.name != appPaymentGateway,
orElse: () => null) ==
null) {
paymentTypes.add(paymentTypeList.firstWhere(
(paymentTypeList) => paymentTypeList.name == appPaymentGateway,
orElse: () => null));
}
});
}
if (!app_payment_gateways.contains('Stripe') && AppHelper.instance.appConfig.stripeEnabled == true) {
paymentTypes.add(paymentTypeList.firstWhere((element) => element.name == "Stripe", orElse: () => null));
if (!app_payment_gateways.contains('Stripe') &&
AppHelper.instance.appConfig.stripeEnabled == true) {
paymentTypes.add(paymentTypeList
.firstWhere((element) => element.name == "Stripe", orElse: () => null));
}
if (!app_payment_gateways.contains('PayPal') && AppHelper.instance.appConfig.paypalEnabled == true) {
paymentTypes.add(paymentTypeList.firstWhere((element) => element.name == "PayPal", orElse: () => null));
if (!app_payment_gateways.contains('PayPal') &&
AppHelper.instance.appConfig.paypalEnabled == true) {
paymentTypes.add(paymentTypeList
.firstWhere((element) => element.name == "PayPal", orElse: () => null));
}
if (!app_payment_gateways.contains('CashOnDelivery') && AppHelper.instance.appConfig.codEnabled == true) {
paymentTypes.add(paymentTypeList.firstWhere((element) => element.name == "CashOnDelivery", orElse: () => null));
if (!app_payment_gateways.contains('CashOnDelivery') &&
AppHelper.instance.appConfig.codEnabled == true) {
paymentTypes.add(paymentTypeList.firstWhere(
(element) => element.name == "CashOnDelivery",
orElse: () => null));
}
return paymentTypes.where((v) => v != null).toList();
}
dynamic envVal(String envVal, {dynamic defaultValue}) => (getEnv(envVal) == null ? defaultValue : getEnv(envVal));
dynamic envVal(String envVal, {dynamic defaultValue}) =>
(getEnv(envVal) == null ? defaultValue : getEnv(envVal));
PaymentType addPayment(
{@required int id,
@required String name,
@required String desc,
@required String assetImage,
@required Function pay}) => PaymentType(
@required Function pay}) =>
PaymentType(
id: id,
name: name,
desc: desc,
@ -333,7 +350,7 @@ RegExp defaultRegex(
String pattern, {
bool strict,
}) {
return new RegExp(
return RegExp(
pattern,
caseSensitive: strict ?? false,
multiLine: false,
@ -343,17 +360,10 @@ RegExp defaultRegex(
bool isEmail(String em) {
String p =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(p);
RegExp regExp = RegExp(p);
return regExp.hasMatch(em);
}
// 6 LENGTH, 1 DIGIT
bool validPassword(String pw) {
String p = r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,}$';
RegExp regExp = new RegExp(p);
return regExp.hasMatch(pw);
}
navigatorPush(BuildContext context,
{@required String routeName,
Object arguments,
@ -391,8 +401,7 @@ showPlatformAlertDialog(BuildContext context,
List<PlatformDialogAction> actions,
bool showDoneAction = true}) {
if (showDoneAction) {
actions
.add(dialogAction(context, title: trans("Done"), action: () {
actions.add(dialogAction(context, title: trans("Done"), action: () {
Navigator.of(context).pop();
}));
}
@ -422,22 +431,22 @@ String dateFormatted({@required String date, @required String formatType}) =>
formatDateTime(formatType).format(parseDateTime(date));
enum FormatType {
DateTime,
Date,
Time,
dateTime,
date,
time,
}
String formatForDateTime(FormatType formatType) {
switch (formatType) {
case FormatType.Date:
case FormatType.date:
{
return "yyyy-MM-dd";
}
case FormatType.DateTime:
case FormatType.dateTime:
{
return "dd-MM-yyyy hh:mm a";
}
case FormatType.Time:
case FormatType.time:
{
return "hh:mm a";
}
@ -450,9 +459,6 @@ String formatForDateTime(FormatType formatType) {
double parseWcPrice(String price) => (double.tryParse(price ?? "0") ?? 0);
void appLogOutput(dynamic message) =>
(getEnv('APP_DEBUG', defaultValue: true) ? NyLogger.debug(message) : null);
Widget refreshableScroll(context,
{@required refreshController,
@required VoidCallback onRefresh,
@ -486,7 +492,7 @@ Widget refreshableScroll(context,
controller: refreshController,
onRefresh: onRefresh,
onLoading: onLoading,
child: (products.length != null && products.length > 0
child: (products.length != null && products.isNotEmpty
? StaggeredGridView.countBuilder(
crossAxisCount: 2,
itemCount: products.length,
@ -494,13 +500,12 @@ Widget refreshableScroll(context,
return Container(
height: 200,
child: ProductItemContainer(
index: (index),
product: products[index],
onTap: onTap,
),
);
},
staggeredTileBuilder: (int index) => new StaggeredTile.fit(1),
staggeredTileBuilder: (int index) => StaggeredTile.fit(1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
@ -538,3 +543,31 @@ Future<List<DefaultShipping>> getDefaultShipping(BuildContext context) async {
String truncateString(String data, int length) {
return (data.length >= length) ? '${data.substring(0, length)}...' : data;
}
Future<List<dynamic>> getWishlistProducts() async {
List<dynamic> favouriteProducts = [];
String currentProductsJSON = await NyStorage.read(SharedKey.wishlistProducts);
if (currentProductsJSON != null) {
favouriteProducts =
(jsonDecode(currentProductsJSON) as List<dynamic>).toList();
}
return favouriteProducts;
}
saveWishlistProduct({@required Product product}) async {
List<dynamic> products = await getWishlistProducts();
if (products.any((wishListProduct) => wishListProduct['id'] == product.id) ==
false) {
products.add({"id": product.id});
}
String json = jsonEncode(products.map((i) => {"id": i['id']}).toList());
await NyStorage.store(SharedKey.wishlistProducts, json);
}
removeWishlistProduct({@required Product product}) async {
List<dynamic> products = await getWishlistProducts();
products.removeWhere((element) => element['id'] == product.id);
String json = jsonEncode(products.map((i) => {"id": i['id']}).toList());
await NyStorage.store(SharedKey.wishlistProducts, json);
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -13,4 +13,5 @@ class SharedKey {
static const String cart = "CART_SESSION";
static const String customerBillingDetails = "CS_BILLING_DETAILS";
static const String customerShippingDetails = "CS_SHIPPING_DETAILS";
static const String wishlistProducts = "CS_WISHLIST_PRODUCTS";
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software

View File

@ -16,4 +16,4 @@ import 'package:google_fonts/google_fonts.dart';
TextStyle appFont = GoogleFonts.poppins();
// e.g. custom font in pubspec.yaml - https://flutter.dev/docs/cookbook/design/fonts
// final TextStyle appThemeFont = TextStyle(fontFamily: "ZenTokyoZoo");
// final TextStyle appThemeFont = TextStyle(fontFamily: "ZenTokyoZoo");

View File

@ -54,4 +54,4 @@ final String assetsDirectory = 'lang/';
| the asset json files.
|--------------------------------------------------------------------------
*/
final Map<String, String> valuesAsMap = {};
final Map<String, String> valuesAsMap = {};

View File

@ -13,9 +13,7 @@ import 'package:flutter_app/resources/themes/styles/light_theme_colors.dart';
// App Themes
final appThemes = [
ThemeConfig.light(),
ThemeConfig.dark(),
];
@ -39,46 +37,45 @@ BaseColorStyles darkColors = DarkThemeColors();
// Preset Themes
class ThemeConfig {
// LIGHT
static BaseThemeConfig light() => BaseThemeConfig(
id: "default_light_theme",
description: "Light theme",
theme: lightTheme(lightColors),
colors: lightColors,
);
id: "default_light_theme",
description: "Light theme",
theme: lightTheme(lightColors),
colors: lightColors,
);
// DARK
static BaseThemeConfig dark() => BaseThemeConfig(
id: "default_dark_theme",
description: "Dark theme",
theme: darkTheme(darkColors),
colors: darkColors,
);
id: "default_dark_theme",
description: "Dark theme",
theme: darkTheme(darkColors),
colors: darkColors,
);
// E.G. CUSTOM THEME
/// Run: "flutter pub run nylo_framework:main make:theme bright_theme" // example bright_theme
/// Run: "flutter pub run nylo_framework:main make:theme bright_theme" // example bright_theme
// Creates a basic theme in /resources/themes/bright_theme.dart
// Creates the themes colors in /resources/themes/styles/bright_theme_colors.dart
// First add the colors which was created into the above section like the following:
// Bright Colors
/// BaseColorStyles brightColors = BrightThemeColors();
/// BaseColorStyles brightColors = BrightThemeColors();
// Next, uncomment the below:
/// static BaseThemeConfig bright() => BaseThemeConfig(
/// id: "default_bright_theme",
/// description: "Bright theme",
/// theme: brightTheme(brightColors),
/// colors: brightColors,
/// );
/// static BaseThemeConfig bright() => BaseThemeConfig(
/// id: "default_bright_theme",
/// description: "Bright theme",
/// theme: brightTheme(brightColors),
/// colors: brightColors,
/// );
// To then use this theme, add it to the [appThemes] above like the following:
// final appThemes = [
/// ThemeConfig.bright(), // new theme
/// ThemeConfig.bright(), // new theme
//
// ThemeConfig.light(),
//
// ThemeConfig.dark(),
// ];
}
}

View File

@ -0,0 +1,20 @@
//
// Generated file. Do not edit.
//
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars
import 'package:flutter_secure_storage_web/flutter_secure_storage_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
FlutterSecureStorageWeb.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
registrar.registerMessageHandler();
}

View File

@ -9,7 +9,9 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
Nylo nylo = await Nylo.init(router: appRouter(), setup: boot);
String initialRoute = AppHelper.instance.appConfig.appStatus != null ? '/home' : '/no-connection';
String initialRoute = AppHelper.instance.appConfig.appStatus != null
? '/home'
: '/no-connection';
runApp(
AppBuild(

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -32,7 +32,7 @@ class _AccountBillingDetailsPageState extends State<AccountBillingDetailsPage> {
_AccountBillingDetailsPageState();
// BILLING TEXT CONTROLLERS
TextEditingController _txtShippingFirstName = TextEditingController(),
final TextEditingController _txtShippingFirstName = TextEditingController(),
_txtShippingLastName = TextEditingController(),
_txtShippingAddressLine = TextEditingController(),
_txtShippingCity = TextEditingController(),
@ -71,18 +71,15 @@ class _AccountBillingDetailsPageState extends State<AccountBillingDetailsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
trans("Billing Details")
),
title: Text(trans("Billing Details")),
centerTitle: true,
),
body: SafeAreaWidget(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: _isLoading
? AppLoaderWidget()
: LayoutBuilder(
@ -160,8 +157,10 @@ class _AccountBillingDetailsPageState extends State<AccountBillingDetailsPage> {
decoration: BoxDecoration(
color: ThemeColor.get(context).surfaceBackground,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
boxShadow: (Theme.of(context).brightness ==
Brightness.light)
? wsBoxShadow()
: null,
),
padding: EdgeInsets.all(8),
),

View File

@ -1,17 +1,17 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:bubble_tab_indicator/bubble_tab_indicator.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/customer_orders_loader_controller.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
@ -33,15 +33,14 @@ class AccountDetailPage extends StatefulWidget {
class _AccountDetailPageState extends State<AccountDetailPage>
with SingleTickerProviderStateMixin {
RefreshController _refreshController =
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
bool _shouldStopRequests = false,
waitForNextRequest = false,
_isLoading = true,
_isLoadingOrders = true;
final CustomerOrdersLoaderController _customerOrdersLoaderController =
CustomerOrdersLoaderController();
int _page = 1, _currentTabIndex = 0;
List<Order> _orders = [];
bool _shouldStopRequests = false, _isLoading = true, _isLoadingOrders = true;
int _currentTabIndex = 0;
WCCustomerInfoResponse _wcCustomerInfoResponse;
Widget _activeBody;
@ -51,20 +50,18 @@ class _AccountDetailPageState extends State<AccountDetailPage>
@override
void initState() {
super.initState();
_page = 1;
_orders = [];
_tabs = [
new Tab(text: ""),
new Tab(text: ""),
Tab(text: ""),
Tab(text: ""),
];
_tabController = TabController(vsync: this, length: _tabs.length);
_activeBody = AppLoaderWidget();
this.init();
init();
}
init() async {
await _fetchWpUserData();
await _fetchOrders();
await fetchOrders();
}
_fetchWpUserData() async {
@ -98,22 +95,22 @@ class _AccountDetailPageState extends State<AccountDetailPage>
@override
Widget build(BuildContext context) {
_tabs = [
new Tab(text: trans("Orders")),
new Tab(text: trans("Settings")),
Tab(text: trans("Orders")),
Tab(text: trans("Settings")),
];
return Scaffold(
appBar: AppBar(
leading: widget.showLeadingBackButton ? Container(
child: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () => Navigator.pop(context),
),
margin: EdgeInsets.only(left: 0),
) : Container(),
title: Text(
trans("Account")
),
leading: widget.showLeadingBackButton
? Container(
child: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () => Navigator.pop(context),
),
margin: EdgeInsets.only(left: 0),
)
: Container(),
title: Text(trans("Account")),
centerTitle: true,
),
resizeToAvoidBottomInset: false,
@ -191,7 +188,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
indicatorSize: TabBarIndicatorSize.tab,
labelColor: Colors.white,
unselectedLabelColor: Colors.black87,
indicator: new BubbleTabIndicator(
indicator: BubbleTabIndicator(
indicatorHeight: 25.0,
indicatorColor: Colors.black87,
tabBarIndicatorSize: TabBarIndicatorSize.tab,
@ -204,7 +201,10 @@ class _AccountDetailPageState extends State<AccountDetailPage>
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: (Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
boxShadow:
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
color: ThemeColor.get(context).backgroundContainer,
),
),
@ -275,38 +275,34 @@ class _AccountDetailPageState extends State<AccountDetailPage>
);
}
_fetchOrders() async {
fetchOrders() async {
String userId = await readUserId();
if (userId == null || _shouldStopRequests == true) {
if (userId == null) {
setState(() {
_isLoadingOrders = false;
_activeBody = _widgetOrders();
});
return;
}
List<Order> orders = await appWooSignal((api) =>
api.getOrders(customer: int.parse(userId), page: _page, perPage: 50));
if (orders.length <= 0) {
setState(() {
_isLoadingOrders = false;
_shouldStopRequests = true;
_activeBody = _widgetOrders();
});
return;
}
setState(() {
_page += 1;
_orders.addAll(orders);
_isLoadingOrders = false;
_activeBody = _widgetOrders();
});
await _customerOrdersLoaderController.loadOrders(
hasResults: (result) {
if (result == false) {
_isLoadingOrders = false;
_shouldStopRequests = true;
_activeBody = _widgetOrders();
return false;
}
return true;
},
didFinish: () => setState(() {
_isLoadingOrders = false;
_activeBody = _widgetOrders();
}),
userId: userId);
}
Widget _widgetOrders() {
List<Order> orders = _customerOrdersLoaderController.getResults();
return _isLoadingOrders
? AppLoaderWidget()
: SmartRefresher(
@ -335,9 +331,10 @@ class _AccountDetailPageState extends State<AccountDetailPage>
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
child: (_orders.length > 0
child: (orders.isNotEmpty
? ListView.builder(
itemBuilder: (cxt, i) {
Order order = orders[i];
return Card(
child: ListTile(
contentPadding: EdgeInsets.only(
@ -356,12 +353,12 @@ class _AccountDetailPageState extends State<AccountDetailPage>
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"#${_orders[i].id.toString()}",
"#${order.id.toString()}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
_orders[i].status.capitalize(),
order.status.capitalize(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@ -379,8 +376,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
formatStringCurrency(
total: _orders[i].total),
formatStringCurrency(total: order.total),
style: Theme.of(context)
.textTheme
.bodyText2
@ -389,7 +385,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
textAlign: TextAlign.left,
),
Text(
_orders[i].lineItems.length.toString() +
order.lineItems.length.toString() +
" " +
trans("items"),
style: Theme.of(context)
@ -403,15 +399,15 @@ class _AccountDetailPageState extends State<AccountDetailPage>
),
Text(
dateFormatted(
date: _orders[i].dateCreated,
date: order.dateCreated,
formatType:
formatForDateTime(FormatType.Date),
formatForDateTime(FormatType.date),
) +
"\n" +
dateFormatted(
date: _orders[i].dateCreated,
date: order.dateCreated,
formatType:
formatForDateTime(FormatType.Time),
formatForDateTime(FormatType.time),
),
textAlign: TextAlign.right,
style: Theme.of(context)
@ -434,7 +430,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
),
);
},
itemCount: _orders.length,
itemCount: orders.length,
)
: Center(
child: Column(
@ -456,16 +452,15 @@ class _AccountDetailPageState extends State<AccountDetailPage>
}
void _onRefresh() async {
_orders = [];
_page = 1;
_customerOrdersLoaderController.clear();
_shouldStopRequests = false;
waitForNextRequest = false;
await _fetchOrders();
await fetchOrders();
_refreshController.refreshCompleted();
}
void _onLoading() async {
await _fetchOrders();
await fetchOrders();
if (mounted) {
setState(() {});
@ -480,6 +475,6 @@ class _AccountDetailPageState extends State<AccountDetailPage>
_viewProfileDetail(int i) => Navigator.pushNamed(
context,
"/account-order-detail",
arguments: _orders[i].id,
arguments: _customerOrdersLoaderController.getResults()[i].id,
);
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -33,7 +33,7 @@ class AccountLandingPage extends StatefulWidget {
class _AccountLandingPageState extends State<AccountLandingPage> {
bool _hasTappedLogin = false;
TextEditingController _tfEmailController = TextEditingController(),
final TextEditingController _tfEmailController = TextEditingController(),
_tfPasswordController = TextEditingController();
@override
@ -75,7 +75,10 @@ class _AccountLandingPageState extends State<AccountLandingPage> {
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: (Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
boxShadow:
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
color: ThemeColor.get(context).backgroundContainer,
),
padding: EdgeInsets.symmetric(vertical: 18, horizontal: 8),
@ -110,8 +113,9 @@ class _AccountLandingPageState extends State<AccountLandingPage> {
children: <Widget>[
Icon(
Icons.account_circle,
color:
(Theme.of(context).brightness == Brightness.light) ? Colors.black38 : Colors.white70,
color: (Theme.of(context).brightness == Brightness.light)
? Colors.black38
: Colors.white70,
),
Padding(
child: Text(
@ -137,16 +141,19 @@ class _AccountLandingPageState extends State<AccountLandingPage> {
"No URL found for \"forgot password\".\nAdd your forgot password URL here https://woosignal.com/dashboard/apps");
}
}),
widget.showBackButton ? Column(
children: [
Divider(),
LinkButton(
title: trans("Back"),
action: () => Navigator.pop(context),
),
],
) : Padding(padding: EdgeInsets.only(bottom: 20),)
widget.showBackButton
? Column(
children: [
Divider(),
LinkButton(
title: trans("Back"),
action: () => Navigator.pop(context),
),
],
)
: Padding(
padding: EdgeInsets.only(bottom: 20),
)
].where((element) => element != null).toList(),
),
),
@ -164,8 +171,7 @@ class _AccountLandingPageState extends State<AccountLandingPage> {
if (email == "" || password == "") {
showToastNotification(context,
title: trans("Invalid details"),
description:
trans("The email and password field cannot be empty"),
description: trans("The email and password field cannot be empty"),
style: ToastNotificationStyleType.DANGER);
return;
}
@ -190,25 +196,23 @@ class _AccountLandingPageState extends State<AccountLandingPage> {
} on InvalidNonceException catch (_) {
showToastNotification(context,
title: trans("Invalid details"),
description: trans("Something went wrong, please contact our store"),
description:
trans("Something went wrong, please contact our store"),
style: ToastNotificationStyleType.DANGER);
} on InvalidEmailException catch (_) {
showToastNotification(context,
title: trans("Invalid details"),
description:
trans("That email does not match our records"),
description: trans("That email does not match our records"),
style: ToastNotificationStyleType.DANGER);
} on InvalidUsernameException catch (_) {
showToastNotification(context,
title: trans("Invalid details"),
description:
trans("That username does not match our records"),
description: trans("That username does not match our records"),
style: ToastNotificationStyleType.DANGER);
} on IncorrectPasswordException catch (_) {
showToastNotification(context,
title: trans("Invalid details"),
description:
trans("That password does not match our records"),
description: trans("That password does not match our records"),
style: ToastNotificationStyleType.DANGER);
} on Exception catch (_) {
showToastNotification(context,

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -43,7 +43,6 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Container(
@ -53,9 +52,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
),
margin: EdgeInsets.only(left: 0),
),
title: Text(
"${trans("Order").capitalize()} #${_orderId.toString()}"
),
title: Text("${trans("Order").capitalize()} #${_orderId.toString()}"),
centerTitle: true,
),
resizeToAvoidBottomInset: false,
@ -66,10 +63,13 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("${trans("Date Ordered").capitalize()}: " +
dateFormatted(
Text(
"${trans("Date Ordered").capitalize()}: " +
dateFormatted(
date: _order.dateCreated,
formatType: formatForDateTime(FormatType.Date))),
formatType: formatForDateTime(FormatType.date),
),
),
Container(
margin: EdgeInsets.only(top: 10, bottom: 10),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
@ -78,8 +78,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: Text(
"${trans("Ships to").capitalize()}:"),
child: Text("${trans("Ships to").capitalize()}:"),
),
Flexible(
child: Text(
@ -106,7 +105,9 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
color: (Theme.of(context).brightness == Brightness.light)
? Colors.white
: Color(0xFF2C2C2C),

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -30,7 +30,7 @@ class _AccountProfileUpdatePageState extends State<AccountProfileUpdatePage> {
_AccountProfileUpdatePageState();
bool isLoading = true;
TextEditingController _tfFirstName = TextEditingController(),
final TextEditingController _tfFirstName = TextEditingController(),
_tfLastName = TextEditingController();
@override

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'dart:math';
import 'package:flutter/material.dart';
@ -42,12 +41,13 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
_AccountRegistrationPageState();
bool _hasTappedRegister = false;
TextEditingController _tfEmailAddressController = TextEditingController(),
final TextEditingController _tfEmailAddressController =
TextEditingController(),
_tfPasswordController = TextEditingController(),
_tfFirstNameController = TextEditingController(),
_tfLastNameController = TextEditingController();
WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
@override
void initState() {
@ -56,16 +56,13 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
title: Text(
trans("Register")
),
title: Text(trans("Register")),
centerTitle: true,
),
resizeToAvoidBottomInset: false,
@ -129,9 +126,10 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
style: TextStyle(fontWeight: FontWeight.bold)),
],
style: TextStyle(
color: (Theme.of(context).brightness == Brightness.light)
? Colors.black45
: Colors.white70),
color:
(Theme.of(context).brightness == Brightness.light)
? Colors.black45
: Colors.white70),
),
textAlign: TextAlign.center,
),
@ -177,7 +175,7 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
});
String username =
(email.replaceAll(new RegExp(r'([@.])'), "")) + _randomStr(4);
(email.replaceAll(RegExp(r'([@.])'), "")) + _randomStr(4);
WPUserRegisterResponse wpUserRegisterResponse;
try {
@ -196,7 +194,8 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
} on InvalidNonceException catch (_) {
showToastNotification(context,
title: trans("Invalid details"),
description: trans("Something went wrong, please contact our store"),
description:
trans("Something went wrong, please contact our store"),
style: ToastNotificationStyleType.DANGER);
} on ExistingUserLoginException catch (_) {
showToastNotification(context,
@ -257,11 +256,9 @@ class _AccountRegistrationPageState extends State<AccountRegistrationPage> {
subtitle: trans("View Terms and Conditions or Privacy policy"),
actions: [
dialogAction(context,
title: trans("Terms and Conditions"),
action: _viewTermsConditions),
title: trans("Terms and Conditions"), action: _viewTermsConditions),
dialogAction(context,
title: trans("Privacy Policy"),
action: _viewPrivacyPolicy),
title: trans("Privacy Policy"), action: _viewPrivacyPolicy),
],
);
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -33,7 +33,7 @@ class _AccountShippingDetailsPageState
_AccountShippingDetailsPageState();
// BILLING TEXT CONTROLLERS
TextEditingController _txtShippingFirstName = TextEditingController(),
final TextEditingController _txtShippingFirstName = TextEditingController(),
_txtShippingLastName = TextEditingController(),
_txtShippingAddressLine = TextEditingController(),
_txtShippingCity = TextEditingController(),
@ -87,19 +87,16 @@ class _AccountShippingDetailsPageState
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
trans("Shipping Details")
),
title: Text(trans("Shipping Details")),
centerTitle: true,
),
body: SafeAreaWidget(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
FocusScope.of(context).requestFocus(FocusNode());
},
child: _isLoading
? AppLoaderWidget()
@ -177,8 +174,10 @@ class _AccountShippingDetailsPageState
decoration: BoxDecoration(
color: ThemeColor.get(context).surfaceBackground,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
boxShadow: (Theme.of(context).brightness ==
Brightness.light)
? wsBoxShadow()
: null,
),
padding: EdgeInsets.all(8),
),

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -9,6 +9,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/product_category_search_loader_controller.dart';
import 'package:flutter_app/app/controllers/browse_category_controller.dart';
import 'package:flutter_app/bootstrap/enums/sort_enums.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
@ -21,7 +22,7 @@ import 'package:nylo_support/widgets/ny_state.dart';
import 'package:nylo_support/widgets/ny_stateful_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:woosignal/models/response/product_category.dart';
import 'package:woosignal/models/response/products.dart' as WS;
import 'package:woosignal/models/response/products.dart' as ws_product;
class BrowseCategoryPage extends NyStatefulWidget {
final BrowseCategoryController controller = BrowseCategoryController();
@ -35,51 +36,20 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
ProductCategory productCategory;
_BrowseCategoryPageState();
List<WS.Product> _products = [];
RefreshController _refreshController =
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
final ProductCategorySearchLoaderController
_productCategorySearchLoaderController =
ProductCategorySearchLoaderController();
int _page = 1;
bool _shouldStopRequests = false,
waitForNextRequest = false,
_isLoading = true;
bool _shouldStopRequests = false;
bool _isLoading = true;
@override
widgetDidLoad() async {
super.widgetDidLoad();
this.productCategory = widget.controller.data();
await _fetchMoreProducts();
}
_fetchMoreProducts() async {
if (waitForNextRequest || _shouldStopRequests) {
return;
}
waitForNextRequest = true;
List<WS.Product> products = await appWooSignal(
(api) => api.getProducts(
perPage: 50,
category: productCategory.id.toString(),
page: _page,
status: "publish",
stockStatus: "instock"),
);
if (products.length == 0) {
_shouldStopRequests = true;
_isLoading = false;
setState(() {});
return;
} else {
_products.addAll(products);
}
waitForNextRequest = false;
_page = _page + 1;
setState(() {
_isLoading = false;
});
productCategory = widget.controller.data();
await fetchProducts();
}
@override
@ -95,8 +65,7 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(trans("Browse"),
style: Theme.of(context).textTheme.subtitle1),
Text(trans("Browse"), style: Theme.of(context).textTheme.subtitle1),
Text(parseHtmlString(productCategory.name))
],
),
@ -117,23 +86,22 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
refreshController: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
products: _products,
products: _productCategorySearchLoaderController.getResults(),
onTap: _showProduct),
),
);
}
void _onRefresh() async {
_products = [];
_page = 1;
_productCategorySearchLoaderController.clear();
_shouldStopRequests = false;
waitForNextRequest = false;
await _fetchMoreProducts();
await fetchProducts();
_refreshController.refreshCompleted();
}
void _onLoading() async {
await _fetchMoreProducts();
await fetchProducts();
if (mounted) {
setState(() {});
@ -146,26 +114,28 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
}
_sortProducts({@required SortByType by}) {
List<ws_product.Product> products =
_productCategorySearchLoaderController.getResults();
switch (by) {
case SortByType.LowToHigh:
_products.sort(
case SortByType.lowToHigh:
products.sort(
(product1, product2) => (parseWcPrice(product1.price))
.compareTo((parseWcPrice(product2.price))),
);
break;
case SortByType.HighToLow:
_products.sort(
case SortByType.highToLow:
products.sort(
(product1, product2) => (parseWcPrice(product2.price))
.compareTo((parseWcPrice(product1.price))),
);
break;
case SortByType.NameAZ:
_products.sort(
case SortByType.nameAZ:
products.sort(
(product1, product2) => product1.name.compareTo(product2.name),
);
break;
case SortByType.NameZA:
_products.sort(
case SortByType.nameZA:
products.sort(
(product1, product2) => product2.name.compareTo(product1.name),
);
break;
@ -183,28 +153,28 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
children: <Widget>[
LinkButton(
title: trans("Sort: Low to high"),
action: () => _sortProducts(by: SortByType.LowToHigh),
action: () => _sortProducts(by: SortByType.lowToHigh),
),
Divider(
height: 0,
),
LinkButton(
title: trans("Sort: High to low"),
action: () => _sortProducts(by: SortByType.HighToLow),
action: () => _sortProducts(by: SortByType.highToLow),
),
Divider(
height: 0,
),
LinkButton(
title: trans("Sort: Name A-Z"),
action: () => _sortProducts(by: SortByType.NameAZ),
action: () => _sortProducts(by: SortByType.nameAZ),
),
Divider(
height: 0,
),
LinkButton(
title: trans("Sort: Name Z-A"),
action: () => _sortProducts(by: SortByType.NameZA),
action: () => _sortProducts(by: SortByType.nameZA),
),
Divider(
height: 0,
@ -215,11 +185,28 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
);
}
_dismissModal() {
Navigator.pop(context);
Future fetchProducts() async {
await _productCategorySearchLoaderController.loadProducts(
hasResults: (result) {
if (result == false) {
setState(() {
_isLoading = false;
_shouldStopRequests = true;
});
return false;
}
return true;
},
didFinish: () => setState(() {
_isLoading = false;
}),
productCategory: productCategory,
);
}
_showProduct(WS.Product product) {
_dismissModal() => Navigator.pop(context);
_showProduct(ws_product.Product product) {
Navigator.pushNamed(context, "/product-detail", arguments: product);
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -10,6 +10,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/browse_search_controller.dart';
import 'package:flutter_app/app/controllers/product_search_loader_controller.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
@ -17,7 +18,7 @@ import 'package:nylo_support/helpers/helper.dart';
import 'package:nylo_support/widgets/ny_state.dart';
import 'package:nylo_support/widgets/ny_stateful_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:woosignal/models/response/products.dart' as WS;
import 'package:woosignal/models/response/products.dart' as ws_product;
class BrowseSearchPage extends NyStatefulWidget {
final BrowseSearchController controller = BrowseSearchController();
@ -28,54 +29,19 @@ class BrowseSearchPage extends NyStatefulWidget {
}
class _BrowseSearchState extends NyState<BrowseSearchPage> {
RefreshController _refreshController =
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
final ProductSearchLoaderController _productSearchLoaderController =
ProductSearchLoaderController();
List<WS.Product> _products = [];
String _search;
int _page = 1;
bool _shouldStopRequests = false,
waitForNextRequest = false,
_isLoading = true;
bool _shouldStopRequests = false, _isLoading = true;
@override
widgetDidLoad() async {
super.widgetDidLoad();
_search = widget.controller.data();
await _fetchProductsForSearch();
}
_fetchProductsForSearch() async {
if (waitForNextRequest || _shouldStopRequests) {
return;
}
waitForNextRequest = true;
List<WS.Product> products = await appWooSignal(
(api) => api.getProducts(
perPage: 100,
search: _search,
page: _page,
status: "publish",
stockStatus: "instock",
),
);
if (products.length == 0) {
_shouldStopRequests = true;
setState(() {
_isLoading = false;
});
return;
} else {
_products.addAll(products);
}
waitForNextRequest = false;
_page = _page + 1;
setState(() {
_isLoading = false;
});
await fetchProducts();
}
@override
@ -103,27 +69,28 @@ class _BrowseSearchState extends NyState<BrowseSearchPage> {
? Center(
child: AppLoaderWidget(),
)
: refreshableScroll(context,
: refreshableScroll(
context,
refreshController: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
products: _products,
onTap: _showProduct),
products: _productSearchLoaderController.getResults(),
onTap: _showProduct,
),
),
);
}
void _onRefresh() async {
_products = [];
_page = 1;
_productSearchLoaderController.clear();
_shouldStopRequests = false;
waitForNextRequest = false;
await _fetchProductsForSearch();
await fetchProducts();
_refreshController.refreshCompleted();
}
void _onLoading() async {
await _fetchProductsForSearch();
await fetchProducts();
if (mounted) {
setState(() {});
@ -135,7 +102,26 @@ class _BrowseSearchState extends NyState<BrowseSearchPage> {
}
}
_showProduct(WS.Product product) {
Future fetchProducts() async {
await _productSearchLoaderController.loadProducts(
hasResults: (result) {
if (result == false) {
setState(() {
_isLoading = false;
_shouldStopRequests = true;
});
return false;
}
return true;
},
didFinish: () => setState(() {
_isLoading = false;
}),
search: _search,
);
}
_showProduct(ws_product.Product product) {
Navigator.pushNamed(context, "/product-detail", arguments: product);
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -40,11 +40,12 @@ class _CartPageState extends State<CartPage> {
void initState() {
super.initState();
_cartCheck();
CheckoutSession.getInstance.coupon = null;
}
_cartCheck() async {
List<CartLineItem> cart = await Cart.getInstance.getCart();
if (cart.length == 0) {
if (cart.isEmpty) {
setState(() {
_isLoading = false;
_isCartEmpty = true;
@ -56,7 +57,7 @@ class _CartPageState extends State<CartPage> {
List<dynamic> cartRes =
await appWooSignal((api) => api.cartCheck(cartJSON));
if (cartRes.length == 0) {
if (cartRes.isEmpty) {
Cart.getInstance.saveCartToPref(cartLineItems: []);
setState(() {
_isCartEmpty = true;
@ -65,7 +66,7 @@ class _CartPageState extends State<CartPage> {
return;
}
_cartLines = cartRes.map((json) => CartLineItem.fromJson(json)).toList();
if (_cartLines.length > 0) {
if (_cartLines.isNotEmpty) {
Cart.getInstance.saveCartToPref(cartLineItems: _cartLines);
}
setState(() {
@ -80,7 +81,7 @@ class _CartPageState extends State<CartPage> {
return;
}
if (cartLineItems.length == 0) {
if (cartLineItems.isEmpty) {
showToastNotification(
context,
title: trans("Cart"),
@ -162,7 +163,7 @@ class _CartPageState extends State<CartPage> {
style: ToastNotificationStyleType.WARNING,
icon: Icons.remove_shopping_cart,
);
if (_cartLines.length == 0) {
if (_cartLines.isEmpty) {
_isCartEmpty = true;
}
setState(() {});
@ -272,16 +273,17 @@ class _CartPageState extends State<CartPage> {
case ConnectionState.waiting:
return Text("");
default:
if (snapshot.hasError)
if (snapshot.hasError) {
return Text("");
else
return new Padding(
} else {
return Padding(
child: TextRowWidget(
title: trans("Total"),
text: (_isLoading ? "" : snapshot.data),
),
padding: EdgeInsets.only(bottom: 15, top: 15),
);
}
}
},
),

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -11,12 +11,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/app/models/customer_address.dart';
import 'package:flutter_app/app/models/customer_country.dart';
import 'package:flutter_app/app/models/payment_type.dart';
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/buttons.dart';
import 'package:flutter_app/resources/widgets/checkout_coupon_amount_widget.dart';
import 'package:flutter_app/resources/widgets/checkout_payment_type_widget.dart';
import 'package:flutter_app/resources/widgets/checkout_select_coupon_widget.dart';
import 'package:flutter_app/resources/widgets/checkout_shipping_type_widget.dart';
import 'package:flutter_app/resources/widgets/checkout_store_heading_widget.dart';
import 'package:flutter_app/resources/widgets/checkout_user_details_widget.dart';
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
@ -36,17 +42,18 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
bool _showFullLoader = true, _isProcessingPayment = false;
List<TaxRate> _taxRates = [];
final List<TaxRate> _taxRates = [];
TaxRate _taxRate;
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
@override
void initState() {
super.initState();
CheckoutSession.getInstance.coupon = null;
List<PaymentType> paymentTypes = getPaymentTypes();
if (CheckoutSession.getInstance.paymentType == null &&
getPaymentTypes().length > 0) {
CheckoutSession.getInstance.paymentType = getPaymentTypes().first;
paymentTypes.isNotEmpty) {
CheckoutSession.getInstance.paymentType = paymentTypes.first;
}
_getTaxes();
}
@ -64,7 +71,7 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
List<TaxRate> tmpTaxRates = await appWooSignal(
(api) => api.getTaxRates(page: pageIndex, perPage: 100));
if (tmpTaxRates != null && tmpTaxRates.length > 0) {
if (tmpTaxRates != null && tmpTaxRates.isNotEmpty) {
_taxRates.addAll(tmpTaxRates);
}
if (tmpTaxRates.length >= 100) {
@ -74,7 +81,7 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
}
}
if (_taxRates == null || _taxRates.length == 0) {
if (_taxRates == null || _taxRates.isEmpty) {
setState(() {
_showFullLoader = false;
});
@ -131,12 +138,10 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
orElse: () => null,
);
if (taxRate == null) {
taxRate = _taxRates.firstWhere(
(t) => t.country == shippingCountry.countryCode,
orElse: () => null,
);
}
taxRate ??= _taxRates.firstWhere(
(t) => t.country == shippingCountry.countryCode,
orElse: () => null,
);
}
if (taxRate != null) {
@ -147,225 +152,154 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
});
}
_actionCheckoutDetails() {
Navigator.pushNamed(context, "/checkout-details").then((e) {
setState(() {
_showFullLoader = true;
});
_getTaxes();
});
}
_actionPayWith() {
Navigator.pushNamed(context, "/checkout-payment-type")
.then((value) => setState(() {}));
}
_actionSelectShipping() {
CustomerAddress shippingAddress =
CheckoutSession.getInstance.billingDetails.shippingAddress;
if (shippingAddress == null || shippingAddress.customerCountry == null) {
showToastNotification(context,
title: trans("Oops"),
description: trans("Add your shipping details first"),
icon: Icons.local_shipping);
return;
}
Navigator.pushNamed(context, "/checkout-shipping-type").then((value) {
setState(() {});
});
}
@override
Widget build(BuildContext context) {
CheckoutSession checkoutSession = CheckoutSession.getInstance;
if (_showFullLoader == true) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AppLoaderWidget(),
Padding(
padding: const EdgeInsets.only(top: 15),
child: Text(
"${trans("One moment")}...",
style: Theme.of(context).textTheme.subtitle1,
),
)
],
),
);
}
return Scaffold(
appBar: AppBar(
title: Text(
trans("Checkout")
),
title: Text(trans("Checkout")),
centerTitle: true,
leading: Container(
child: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
CheckoutSession.getInstance.coupon = null;
Navigator.pop(context);
},
),
margin: EdgeInsets.only(left: 0),
),
),
resizeToAvoidBottomInset: false,
body: SafeAreaWidget(
child: !_showFullLoader
? Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: ThemeColor.get(context).backgroundContainer,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
),
margin: EdgeInsets.only(top: 5, bottom: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
decoration: BoxDecoration(
boxShadow: (Theme.of(context).brightness == Brightness.light)
? wsBoxShadow(blurRadius: 10)
: null,
color: Colors.transparent,
),
padding: EdgeInsets.all(2),
margin: EdgeInsets.only(top: 16),
child: ClipRRect(
child: StoreLogo(height: 65),
borderRadius: BorderRadius.circular(8),
)),
((CheckoutSession.getInstance.billingDetails != null &&
CheckoutSession.getInstance.billingDetails
.billingAddress !=
null)
? wsCheckoutRow(context,
heading: trans("Billing/shipping details"),
leadImage: Icon(Icons.home),
leadTitle:
(CheckoutSession.getInstance.billingDetails == null ||
CheckoutSession.getInstance
.billingDetails.billingAddress
.hasMissingFields()
? trans("Billing address is incomplete")
: CheckoutSession.getInstance
.billingDetails.billingAddress
.addressFull()),
action: _actionCheckoutDetails,
showBorderBottom: true)
: wsCheckoutRow(context,
heading:
trans("Billing/shipping details"),
leadImage: Icon(Icons.home),
leadTitle: trans("Add billing & shipping details"),
action: _actionCheckoutDetails,
showBorderBottom: true)),
(CheckoutSession.getInstance.paymentType != null
? wsCheckoutRow(context,
heading: trans("Payment method"),
leadImage: Container(
color: Colors.white,
child: Image.asset(
getImageAsset(CheckoutSession
.getInstance.paymentType.assetImage),
width: 70,
),
),
leadTitle: CheckoutSession
.getInstance.paymentType.desc,
action: _actionPayWith,
showBorderBottom: true)
: wsCheckoutRow(context,
heading: trans("Pay with"),
leadImage: Icon(Icons.payment),
leadTitle: trans("Select a payment method"),
action: _actionPayWith,
showBorderBottom: true)),
_wooSignalApp.disableShipping == 1
? null
: (CheckoutSession.getInstance.shippingType !=
null
? wsCheckoutRow(context,
heading:
trans("Shipping selected"),
leadImage: Icon(Icons.local_shipping),
leadTitle: CheckoutSession
.getInstance.shippingType
.getTitle(),
action: _actionSelectShipping)
: wsCheckoutRow(
context,
heading:
trans("Select shipping"),
leadImage: Icon(Icons.local_shipping),
leadTitle: trans("Select a shipping option"),
action: _actionSelectShipping,
)),
].where((e) => e != null).toList()),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Divider(
color: Colors.black12,
thickness: 1,
),
wsCheckoutSubtotalWidgetFB(
title: trans("Subtotal"),
),
_wooSignalApp.disableShipping == 1
? null
: widgetCheckoutMeta(context,
title: trans("Shipping fee"),
amount:
CheckoutSession.getInstance.shippingType ==
null
? trans("Select shipping")
: CheckoutSession.getInstance.shippingType
.getTotal(withFormatting: true)),
(_taxRate != null
? wsCheckoutTaxAmountWidgetFB(taxRate: _taxRate)
: null),
wsCheckoutTotalWidgetFB(
title: trans("Total"), taxRate: _taxRate),
Divider(
color: Colors.black12,
thickness: 1,
),
].where((e) => e != null).toList(),
),
PrimaryButton(
title: _isProcessingPayment
? "${trans("PROCESSING")}..."
: trans("CHECKOUT"),
action: _isProcessingPayment ? null : _handleCheckout,
),
],
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AppLoaderWidget(),
Padding(
padding: const EdgeInsets.only(top: 15),
child: Text(
"${trans("One moment")}...",
style: Theme.of(context).textTheme.subtitle1,
),
)
],
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: ThemeColor.get(context).backgroundContainer,
borderRadius: BorderRadius.circular(10),
boxShadow: (Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
),
margin: EdgeInsets.only(top: 5, bottom: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CheckoutStoreHeadingWidget(),
CheckoutUserDetailsWidget(
context: context,
checkoutSession: checkoutSession,
resetState: () {
setState(() {
_showFullLoader = true;
});
_getTaxes();
},
),
CheckoutPaymentTypeWidget(
context: context,
checkoutSession: checkoutSession,
resetState: () => setState(() {}),
),
CheckoutShippingTypeWidget(
context: context,
checkoutSession: checkoutSession,
resetState: () => setState(() {}),
wooSignalApp: _wooSignalApp,
),
].where((e) => e != null).toList()),
),
),
if (_wooSignalApp.couponEnabled == true)
CheckoutSelectCouponWidget(
context: context,
checkoutSession: checkoutSession,
resetState: () => setState(() {}),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Divider(
color: Colors.black12,
thickness: 1,
),
CheckoutSubtotal(
title: trans("Subtotal"),
),
CheckoutCouponAmountWidget(checkoutSession: checkoutSession),
_wooSignalApp.disableShipping == 1
? null
: CheckoutMetaLine(
title: trans("Shipping fee"),
amount: CheckoutSession.getInstance.shippingType == null
? trans("Select shipping")
: CheckoutSession.getInstance.shippingType
.getTotal(withFormatting: true)),
(_taxRate != null ? CheckoutTaxTotal(taxRate: _taxRate) : null),
CheckoutTotal(title: trans("Total"), taxRate: _taxRate),
Divider(
color: Colors.black12,
thickness: 1,
),
].where((e) => e != null).toList(),
),
PrimaryButton(
title: _isProcessingPayment
? "${trans("PROCESSING")}..."
: trans("CHECKOUT"),
action: _isProcessingPayment ? null : _handleCheckout,
),
],
),
),
);
}
_handleCheckout() async {
if (CheckoutSession.getInstance.billingDetails.billingAddress == null) {
CheckoutSession checkoutSession = CheckoutSession.getInstance;
if (checkoutSession.billingDetails.billingAddress == null) {
showToastNotification(
context,
title: trans("Oops"),
description: trans("Please select add your billing/shipping address to proceed"),
description:
trans("Please select add your billing/shipping address to proceed"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.local_shipping,
);
return;
}
if (CheckoutSession.getInstance.billingDetails.billingAddress
.hasMissingFields()) {
if (checkoutSession.billingDetails.billingAddress.hasMissingFields()) {
showToastNotification(
context,
title: trans("Oops"),
description:
trans("Your billing/shipping details are incomplete"),
description: trans("Your billing/shipping details are incomplete"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.local_shipping,
);
@ -373,24 +307,22 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
}
if (_wooSignalApp.disableShipping == 0 &&
CheckoutSession.getInstance.shippingType == null) {
checkoutSession.shippingType == null) {
showToastNotification(
context,
title: trans("Oops"),
description:
trans("Please select a shipping method to proceed"),
description: trans("Please select a shipping method to proceed"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.local_shipping,
);
return;
}
if (CheckoutSession.getInstance.paymentType == null) {
if (checkoutSession.paymentType == null) {
showToastNotification(
context,
title: trans("Oops"),
description:
trans("Please select a payment method to proceed"),
description: trans("Please select a payment method to proceed"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.payment,
);
@ -398,20 +330,20 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
}
if (_wooSignalApp.disableShipping == 0 &&
CheckoutSession.getInstance.shippingType?.minimumValue != null) {
checkoutSession.shippingType?.minimumValue != null) {
String total = await Cart.getInstance.getTotal();
if (total == null) {
return;
}
double doubleTotal = double.parse(total);
double doubleMinimumValue =
double.parse(CheckoutSession.getInstance.shippingType?.minimumValue);
double.parse(checkoutSession.shippingType?.minimumValue);
if (doubleTotal < doubleMinimumValue) {
showToastNotification(context,
title: trans("Sorry"),
description:
"${trans("Spend a minimum of")} ${formatDoubleCurrency(total: doubleMinimumValue)} ${trans("for")} ${CheckoutSession.getInstance.shippingType.getTitle()}",
"${trans("Spend a minimum of")} ${formatDoubleCurrency(total: doubleMinimumValue)} ${trans("for")} ${checkoutSession.shippingType.getTitle()}",
style: ToastNotificationStyleType.INFO,
duration: Duration(seconds: 3));
return;
@ -423,7 +355,7 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
if (!appStatus) {
showToastNotification(context,
title: trans("Sorry"),
description: "${trans("Retry later")}",
description: trans("Retry later"),
style: ToastNotificationStyleType.INFO,
duration: Duration(seconds: 3));
return;
@ -437,13 +369,11 @@ class CheckoutConfirmationPageState extends State<CheckoutConfirmationPage> {
_isProcessingPayment = true;
});
await CheckoutSession.getInstance.paymentType
await checkoutSession.paymentType
.pay(context, state: this, taxRate: _taxRate);
Future.delayed(Duration(milliseconds: 5000), () {
setState(() {
_isProcessingPayment = false;
});
setState(() {
_isProcessingPayment = false;
});
}
}

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/billing_details.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
@ -37,7 +36,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
int activeTabIndex = 0;
// TEXT CONTROLLERS
TextEditingController
final TextEditingController
// billing
_txtBillingFirstName = TextEditingController(),
_txtBillingLastName = TextEditingController(),
@ -168,7 +167,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
),
body: SafeAreaWidget(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -194,8 +193,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
MainAxisAlignment.spaceAround,
children: <Widget>[
SwitchAddressTab(
title:
trans("Billing Details"),
title: trans("Billing Details"),
currentTabIndex: activeTabIndex,
type: "billing",
onTapAction: () => setState(() {
@ -227,7 +225,9 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
color: ThemeColor.get(context).backgroundContainer,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
),
padding: EdgeInsets.only(left: 8, right: 8, top: 8),
child: (activeTab ?? tabBillingDetails()),
@ -315,7 +315,8 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
showToastNotification(
context,
title: trans("Oops"),
description: trans("Invalid shipping address, please check your shipping details"),
description: trans(
"Invalid shipping address, please check your shipping details"),
style: ToastNotificationStyleType.WARNING,
);
return;
@ -324,7 +325,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
// Email validation
String billingEmail = _txtBillingEmailAddress.text;
String shippingEmail = _txtShippingEmailAddress.text;
if (billingEmail.length > 0 && !validate.isEmail(billingEmail)) {
if (billingEmail.isNotEmpty && !validate.isEmail(billingEmail)) {
showToastNotification(
context,
title: trans("Oops"),
@ -334,7 +335,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
return;
}
if (shippingEmail.length > 0 && !validate.isEmail(shippingEmail)) {
if (shippingEmail.isNotEmpty && !validate.isEmail(shippingEmail)) {
showToastNotification(
context,
title: trans("Oops"),
@ -380,7 +381,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
city: "",
postalCode: "",
emailAddress: "",
customerCountry: new CustomerCountry());
customerCountry: CustomerCountry());
}
setState(() {});
}
@ -393,7 +394,7 @@ class _CheckoutDetailsPageState extends State<CheckoutDetailsPage> {
@required String postalCode,
@required String emailAddress,
@required CustomerCountry customerCountry}) {
CustomerAddress customerShippingAddress = new CustomerAddress();
CustomerAddress customerShippingAddress = CustomerAddress();
customerShippingAddress.firstName = firstName;
customerShippingAddress.lastName = lastName;
customerShippingAddress.addressLine = addressLine;

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/app/models/payment_type.dart';
@ -35,7 +34,7 @@ class _CheckoutPaymentTypePageState extends State<CheckoutPaymentTypePage> {
super.initState();
if (CheckoutSession.getInstance.paymentType == null) {
if (getPaymentTypes() != null && getPaymentTypes().length > 0) {
if (getPaymentTypes() != null && getPaymentTypes().isNotEmpty) {
CheckoutSession.getInstance.paymentType = getPaymentTypes().first;
}
}
@ -44,21 +43,21 @@ class _CheckoutPaymentTypePageState extends State<CheckoutPaymentTypePage> {
@override
Widget build(BuildContext context) {
List<PaymentType> paymentTypes = getPaymentTypes();
if (paymentTypes.length == 0 && getEnv('APP_DEBUG', defaultValue: false) == true) {
NyLogger.info('You have no payment methods set. Visit the WooSignal dashboard (https://woosignal.com/dashboard) to set a payment method.');
if (paymentTypes.isEmpty &&
getEnv('APP_DEBUG', defaultValue: false) == true) {
NyLogger.info(
'You have no payment methods set. Visit the WooSignal dashboard (https://woosignal.com/dashboard) to set a payment method.');
}
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
trans("Payment Method")
),
title: Text(trans("Payment Method")),
automaticallyImplyLeading: false,
centerTitle: true,
),
body: SafeAreaWidget(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: LayoutBuilder(
builder: (context, constraints) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -78,53 +77,60 @@ class _CheckoutPaymentTypePageState extends State<CheckoutPaymentTypePage> {
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: paymentTypes.length == 0 ? Container(
padding: EdgeInsets.only(top: 20),
child: Text(trans("No payment methods are available"), style: Theme.of(context).textTheme.bodyText1,),
) : ListView.separated(
itemCount: paymentTypes.length,
itemBuilder: (BuildContext context, int index) {
PaymentType paymentType =
paymentTypes[index];
return ListTile(
contentPadding: EdgeInsets.only(
top: 10,
bottom: 10,
left: 8,
right: 8,
),
leading: Container(
decoration: BoxDecoration(
color: Colors.white
),
padding: EdgeInsets.all(4),
child: Image.asset(
getImageAsset(paymentType.assetImage),
width: 60,
fit: BoxFit.contain,
alignment: Alignment.center,
),
),
title: Text(paymentType.desc,
child: paymentTypes.isEmpty
? Container(
padding: EdgeInsets.only(top: 20),
child: Text(
trans("No payment methods are available"),
style:
Theme.of(context).textTheme.subtitle1),
selected: true,
trailing:
(CheckoutSession.getInstance.paymentType ==
paymentType
? Icon(Icons.check)
: null),
onTap: () {
CheckoutSession.getInstance.paymentType =
paymentType;
Navigator.pop(context);
},
);
},
separatorBuilder: (cxt, i) => Divider(
color: Colors.black12,
),
),
Theme.of(context).textTheme.bodyText1,
),
)
: ListView.separated(
itemCount: paymentTypes.length,
itemBuilder:
(BuildContext context, int index) {
PaymentType paymentType =
paymentTypes[index];
return ListTile(
contentPadding: EdgeInsets.only(
top: 10,
bottom: 10,
left: 8,
right: 8,
),
leading: Container(
decoration:
BoxDecoration(color: Colors.white),
padding: EdgeInsets.all(4),
child: Image.asset(
getImageAsset(paymentType.assetImage),
width: 60,
fit: BoxFit.contain,
alignment: Alignment.center,
),
),
title: Text(paymentType.desc,
style: Theme.of(context)
.textTheme
.subtitle1),
selected: true,
trailing: (CheckoutSession
.getInstance.paymentType ==
paymentType
? Icon(Icons.check)
: null),
onTap: () {
CheckoutSession.getInstance
.paymentType = paymentType;
Navigator.pop(context);
},
);
},
separatorBuilder: (cxt, i) => Divider(
color: Colors.black12,
),
),
),
LinkButton(
title: trans("CANCEL"),
@ -136,7 +142,9 @@ class _CheckoutPaymentTypePageState extends State<CheckoutPaymentTypePage> {
color: ThemeColor.get(context).backgroundContainer,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
),
padding: EdgeInsets.all(8),
),

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/app/models/cart_line_item.dart';
@ -36,7 +35,7 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
_CheckoutShippingTypePageState();
bool _isShippingSupported = true, _isLoading = true;
List<Map<String, dynamic>> _wsShippingOptions = [];
final List<Map<String, dynamic>> _wsShippingOptions = [];
WSShipping _shipping;
@override
@ -105,7 +104,7 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
.firstWhere((element) => element.parentId == 0, orElse: () => null);
await _handleShippingZones(noZones);
}
if (_wsShippingOptions.length == 0) {
if (_wsShippingOptions.isEmpty) {
_isShippingSupported = false;
}
@ -237,15 +236,13 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
trans("Shipping Methods")
),
title: Text(trans("Shipping Methods")),
automaticallyImplyLeading: false,
centerTitle: true,
),
body: SafeAreaWidget(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: LayoutBuilder(
builder: (context, constraints) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -253,9 +250,12 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
children: <Widget>[
Padding(
child: Center(
child: Image.asset(getImageAsset('shipping_icon.png'),
child: Image.asset(
getImageAsset('shipping_icon.png'),
height: 100,
color: (Theme.of(context).brightness == Brightness.light) ? null : Colors.white,
color: (Theme.of(context).brightness == Brightness.light)
? null
: Colors.white,
fit: BoxFit.fitHeight,
),
),
@ -325,8 +325,8 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
"object"]
is FreeShipping
? TextSpan(
text:
trans("Free postage"),
text: trans(
"Free postage"),
)
: TextSpan(
text:
@ -376,7 +376,8 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
),
)
: Text(
trans("Shipping is not supported for your country, sorry"),
trans(
"Shipping is not supported for your country, sorry"),
style:
Theme.of(context).textTheme.headline6,
textAlign: TextAlign.center,
@ -391,7 +392,9 @@ class _CheckoutShippingTypePageState extends State<CheckoutShippingTypePage> {
color: ThemeColor.get(context).backgroundContainer,
borderRadius: BorderRadius.circular(10),
boxShadow:
(Theme.of(context).brightness == Brightness.light) ? wsBoxShadow() : null,
(Theme.of(context).brightness == Brightness.light)
? wsBoxShadow()
: null,
),
padding: EdgeInsets.all(8),
),

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/checkout_status_controller.dart';
import 'package:flutter_app/app/models/cart.dart';
@ -17,7 +16,7 @@ import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/buttons.dart';
import 'package:nylo_support/widgets/ny_state.dart';
import 'package:nylo_support/widgets/ny_stateful_widget.dart';
import 'package:woosignal/models/response/order.dart' as WS;
import 'package:woosignal/models/response/order.dart' as ws_order;
import 'package:nylo_support/helpers/helper.dart';
import '../widgets/woosignal_ui.dart';
@ -31,7 +30,7 @@ class CheckoutStatusPage extends NyStatefulWidget {
}
class _CheckoutStatusState extends NyState<CheckoutStatusPage> {
WS.Order _order;
ws_order.Order _order;
@override
widgetDidLoad() async {
@ -43,7 +42,6 @@ class _CheckoutStatusState extends NyState<CheckoutStatusPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
@ -94,7 +92,10 @@ class _CheckoutStatusState extends NyState<CheckoutStatusPage> {
border: Border(
bottom: BorderSide(color: Colors.black12, width: 1.0),
),
color: (Theme.of(context).brightness == Brightness.light) ? Colors.white : null),
color:
(Theme.of(context).brightness == Brightness.light)
? Colors.white
: null),
padding: EdgeInsets.only(bottom: 20),
),
Container(
@ -121,11 +122,11 @@ class _CheckoutStatusState extends NyState<CheckoutStatusPage> {
alignment: Alignment.center,
),
Expanded(
child: new ListView.builder(
child: ListView.builder(
itemCount:
_order.lineItems == null ? 0 : _order.lineItems.length,
itemBuilder: (BuildContext context, int index) {
WS.LineItems lineItem = _order.lineItems[index];
ws_order.LineItems lineItem = _order.lineItems[index];
return Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
@ -164,13 +165,16 @@ class _CheckoutStatusState extends NyState<CheckoutStatusPage> {
padding: EdgeInsets.all(16),
margin: EdgeInsets.all(8),
color:
(Theme.of(context).brightness == Brightness.light) ? Colors.white : null);
(Theme.of(context).brightness == Brightness.light)
? Colors.white
: null);
}),
),
Align(
child: LinkButton(
title: trans("Back to Home"),
action: () => Navigator.pushNamed(context, "/home"),
action: () =>
Navigator.pushReplacementNamed(context, "/home"),
),
alignment: Alignment.bottomCenter,
),

View File

@ -0,0 +1,223 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/app/models/cart_line_item.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/buttons.dart';
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/coupon.dart';
class CouponPage extends StatefulWidget {
@override
_CouponPageState createState() => _CouponPageState();
}
class _CouponPageState extends State<CouponPage> {
List<Coupon> _coupons = [];
bool _isLoading = false;
final couponController = TextEditingController();
_showAlert({String message, ToastNotificationStyleType style}) {
showToastNotification(
context,
title: trans('Coupon'),
description: message,
style: style ?? ToastNotificationStyleType.SUCCESS,
icon: Icons.call_to_action,
);
}
_successAddCoupon(Coupon coupon) {
_showAlert(message: trans("Added to checkout"));
CheckoutSession.getInstance.coupon = coupon;
Navigator.of(context).pop();
}
Future<void> findCoupon(String couponCode) async {
setState(() {
_isLoading = true;
});
_coupons = await appWooSignal(
(api) => api.getCoupons(code: couponCode, perPage: 100),
);
setState(() {
_isLoading = false;
});
}
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
CheckoutSession checkoutSession = CheckoutSession.getInstance;
return Scaffold(
body: SafeAreaWidget(
child: Column(
children: [
SizedBox(
height: 30,
),
Icon(Icons.local_offer_outlined, size: 30),
Text(
trans('Redeem Coupon'),
style: Theme.of(context).textTheme.headline4,
),
SizedBox(
height: 30,
),
Form(
key: _formKey,
child: TextFormField(
autofocus: true,
controller: couponController,
validator: (value) {
if (value.isEmpty) {
return trans('Please enter coupon to redeem');
}
return null;
},
decoration: InputDecoration(
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 0.0),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 0.0),
),
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(8.0),
),
borderSide: BorderSide(
color: ThemeColor.get(context).primaryAccent)),
filled: true,
hintStyle: TextStyle(color: Colors.grey[800]),
hintText: trans('Add coupon code'),
fillColor: Colors.grey[200],
),
),
),
SizedBox(
height: 25,
),
(_isLoading == true)
? AppLoaderWidget()
: PrimaryButton(
action: () => _applyCoupon(checkoutSession),
title: trans('Apply'),
),
LinkButton(
title: trans("Cancel"), action: () => Navigator.pop(context)),
],
),
),
);
}
_applyCoupon(CheckoutSession checkoutSession) async {
await findCoupon(couponController.text);
if (_formKey.currentState.validate()) {
// No coupons found
if (_coupons.isEmpty) {
_showAlert(
message: "${trans('Coupon not found')}.",
style: ToastNotificationStyleType.WARNING);
return;
}
Coupon coupon = _coupons.first;
DateTime dateNow = DateTime.now();
List<CartLineItem> cart = await Cart.getInstance.getCart();
List<int> productIds = cart.map((e) => e.productId).toList();
// Check excludedProductIds
for (var productId in productIds) {
if (coupon.excludedProductIds.contains(productId)) {
_showAlert(
message:
"${trans('Sorry, this coupon can not be used with your cart')}.",
style: ToastNotificationStyleType.INFO);
return;
}
}
// Check email restrictions
String emailAddress =
checkoutSession.billingDetails.billingAddress.emailAddress;
if (coupon.emailRestrictions.contains(emailAddress)) {
_showAlert(
message: trans('You cannot redeem this coupon'),
style: ToastNotificationStyleType.DANGER);
return;
}
// Check for minimum amount
double minimumAmount = double.parse(coupon.minimumAmount);
String strSubtotal = await Cart.getInstance.getSubtotal();
double doubleSubtotal = double.parse(strSubtotal);
if (minimumAmount != 0 && doubleSubtotal < minimumAmount) {
_showAlert(
message: trans("Spend a minimum of minimumAmount to redeem",
arguments: {"minimumAmount": minimumAmount.toString()}),
style: ToastNotificationStyleType.DANGER);
return;
}
// Check maximum amount
double maximumAmount = double.parse(coupon.maximumAmount);
if (maximumAmount != 0 && doubleSubtotal > maximumAmount) {
_showAlert(
message: trans("Spend less than maximumAmount to redeem",
arguments: {"maximumAmount": maximumAmount.toString()}),
style: ToastNotificationStyleType.DANGER);
return;
}
// Check if coupon has expired
if (coupon.dateExpires != null &&
dateNow.isAfter(
DateTime.parse(coupon.dateExpires),
)) {
_showAlert(
message: trans("This coupon has expired"),
style: ToastNotificationStyleType.WARNING);
return;
}
// Check usage limit
if (coupon.usageLimit != null && coupon.usageCount >= coupon.usageLimit) {
_showAlert(
message: trans("Usage limit has been reached"),
style: ToastNotificationStyleType.WARNING);
return;
}
// Check usage limit per user
int limitPerUser = coupon.usageLimitPerUser;
if (limitPerUser != null &&
coupon.usedBy
.map((e) => e.toLowerCase())
.where((usedBy) => usedBy == emailAddress.toLowerCase())
.length >=
limitPerUser) {
_showAlert(
message: "${trans('You cannot redeem this coupon')}.",
style: ToastNotificationStyleType.WARNING);
return;
}
_successAddCoupon(coupon);
}
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -26,7 +26,7 @@ class _CustomerCountriesPageState extends State<CustomerCountriesPage> {
_CustomerCountriesPageState();
List<DefaultShipping> _defaultShipping = [], _activeShippingResults = [];
TextEditingController _tfSearchCountry = TextEditingController();
final TextEditingController _tfSearchCountry = TextEditingController();
@override
void initState() {
@ -45,9 +45,7 @@ class _CustomerCountriesPageState extends State<CustomerCountriesPage> {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(
trans("Select a country")
),
title: Text(trans("Select a country")),
centerTitle: true,
),
body: SafeAreaWidget(
@ -57,17 +55,16 @@ class _CustomerCountriesPageState extends State<CustomerCountriesPage> {
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
margin: EdgeInsets.only(bottom: 10, top: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(0, 2),
),
],
color: ThemeColor.get(context).background
),
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 2,
blurRadius: 3,
offset: Offset(0, 2),
),
],
color: ThemeColor.get(context).background),
height: 60,
child: Row(
children: [

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -10,6 +10,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/resources/widgets/compo_theme_widget.dart';
import 'package:flutter_app/resources/widgets/mello_theme_widget.dart';
import 'package:flutter_app/resources/widgets/notic_theme_widget.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
@ -34,6 +35,9 @@ class _HomePageState extends State<HomePage> {
if (AppHelper.instance.appConfig.theme == "notic") {
theme = NoticThemeWidget(globalKey: _key, wooSignalApp: _wooSignalApp);
}
if (AppHelper.instance.appConfig.theme == "compo") {
theme = CompoThemeWidget(globalKey: _key, wooSignalApp: _wooSignalApp);
}
return theme;
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -26,7 +26,7 @@ class HomeSearchPage extends StatefulWidget {
class _HomeSearchPageState extends State<HomeSearchPage> {
_HomeSearchPageState();
TextEditingController _txtSearchController = TextEditingController();
final TextEditingController _txtSearchController = TextEditingController();
@override
void initState() {
@ -37,7 +37,8 @@ class _HomeSearchPageState extends State<HomeSearchPage> {
Navigator.pushNamed(context, "/product-search",
arguments: _txtSearchController.text)
.then((search) {
if (AppHelper.instance.appConfig.theme != "notic") {
if (["notic", "compo"].contains(AppHelper.instance.appConfig.theme) ==
false) {
Navigator.pop(context);
}
});

View File

@ -1,7 +1,7 @@
// StoreMob
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -29,7 +29,9 @@ class _NoConnectionPageState extends State<NoConnectionPage> {
@override
void initState() {
super.initState();
print('WooCommerce site is not connected');
if (getEnv('APP_DEBUG') == true) {
NyLogger.error('WooCommerce site is not connected');
}
}
@override
@ -67,8 +69,7 @@ class _NoConnectionPageState extends State<NoConnectionPage> {
if (wooSignalApp == null) {
showToastNotification(context,
title: trans("Oops"),
description: trans("Retry later"));
title: trans("Oops"), description: trans("Retry later"));
return;
}

View File

@ -1,18 +1,18 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_app/app/controllers/product_detail_controller.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/app/models/cart_line_item.dart';
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/buttons.dart';
@ -20,11 +20,14 @@ import 'package:flutter_app/resources/widgets/cached_image_widget.dart';
import 'package:flutter_app/resources/widgets/cart_icon_widget.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/product_variation.dart' as WS;
import 'package:woosignal/models/response/products.dart' as WSProduct;
import 'package:woosignal/models/response/product_variation.dart'
as ws_product_variation;
import 'package:woosignal/models/response/products.dart' as ws_product;
import 'package:flutter_swiper/flutter_swiper.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
class ProductDetailPage extends NyStatefulWidget {
@override
final ProductDetailController controller = ProductDetailController();
ProductDetailPage({Key key}) : super(key: key);
@ -34,10 +37,12 @@ class ProductDetailPage extends NyStatefulWidget {
class _ProductDetailState extends NyState<ProductDetailPage> {
bool _isLoading = false;
WSProduct.Product _product;
ws_product.Product _product;
bool isInFavourites = false;
int _quantityIndicator = 1;
List<WS.ProductVariation> _productVariations = [];
Map<int, dynamic> _tmpAttributeObj = {};
List<ws_product_variation.ProductVariation> _productVariations = [];
final Map<int, dynamic> _tmpAttributeObj = {};
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
@override
widgetDidLoad() async {
@ -50,16 +55,16 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
}
_fetchProductVariations() async {
List<WS.ProductVariation> tmpVariations = [];
List<ws_product_variation.ProductVariation> tmpVariations = [];
int currentPage = 1;
bool isFetching = true;
while (isFetching) {
List<WS.ProductVariation> tmp = await appWooSignal(
List<ws_product_variation.ProductVariation> tmp = await appWooSignal(
(api) => api.getProductVariations(_product.id,
perPage: 100, page: currentPage),
);
if (tmp != null && tmp.length > 0) {
if (tmp != null && tmp.isNotEmpty) {
tmpVariations.addAll(tmp);
}
@ -75,25 +80,25 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
});
}
WS.ProductVariation findProductVariation() {
WS.ProductVariation tmpProductVariation;
ws_product_variation.ProductVariation findProductVariation() {
ws_product_variation.ProductVariation tmpProductVariation;
Map<String, dynamic> tmpSelectedObj = {};
(_tmpAttributeObj.values).forEach((attributeObj) {
for (var attributeObj in _tmpAttributeObj.values) {
tmpSelectedObj[attributeObj["name"]] = attributeObj["value"];
});
}
_productVariations.forEach((productVariation) {
for (var productVariation in _productVariations) {
Map<String, dynamic> tmpVariations = {};
productVariation.attributes.forEach((attr) {
for (var attr in productVariation.attributes) {
tmpVariations[attr.name] = attr.option;
});
}
if (tmpVariations.toString() == tmpSelectedObj.toString()) {
tmpProductVariation = productVariation;
}
});
}
return tmpProductVariation;
}
@ -101,8 +106,7 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
_modalBottomSheetOptionsForAttribute(int attributeIndex) {
wsModalBottom(
context,
title:
"${trans("Select a")} ${_product.attributes[attributeIndex].name}",
title: "${trans("Select a")} ${_product.attributes[attributeIndex].name}",
bodyWidget: ListView.separated(
itemCount: _product.attributes[attributeIndex].options.length,
separatorBuilder: (BuildContext context, int index) => Divider(),
@ -144,7 +148,8 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
}
_modalBottomSheetAttributes() {
WS.ProductVariation productVariation = findProductVariation();
ws_product_variation.ProductVariation productVariation =
findProductVariation();
wsModalBottom(
context,
title: trans("Options"),
@ -203,8 +208,7 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
_tmpAttributeObj.values.length) {
showToastNotification(context,
title: trans("Oops"),
description:
trans("Please select valid options first"),
description: trans("Please select valid options first"),
style: ToastNotificationStyleType.WARNING);
return;
}
@ -212,8 +216,7 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
if (productVariation == null) {
showToastNotification(context,
title: trans("Oops"),
description:
trans("Product variation does not exist"),
description: trans("Product variation does not exist"),
style: ToastNotificationStyleType.WARNING);
return;
}
@ -221,8 +224,7 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
if (productVariation.stockStatus != "instock") {
showToastNotification(context,
title: trans("Sorry"),
description:
trans("This item is not in stock"),
description: trans("This item is not in stock"),
style: ToastNotificationStyleType.WARNING);
return;
}
@ -232,27 +234,11 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
options.add("${v["name"]}: ${v["value"]}");
});
CartLineItem cartLineItem = CartLineItem(
name: _product.name,
productId: _product.id,
variationId: productVariation.id,
quantity: _quantityIndicator,
taxStatus: productVariation.taxStatus,
shippingClassId:
productVariation.shippingClassId.toString(),
subtotal: productVariation.price,
stockQuantity: productVariation.stockQuantity,
isManagedStock: productVariation.manageStock,
taxClass: productVariation.taxClass,
imageSrc: (productVariation.image != null
? productVariation.image.src
: _product.images.length == 0
? getEnv("PRODUCT_PLACEHOLDER_IMAGE")
: _product.images.first.src),
shippingIsTaxable: _product.shippingTaxable,
variationOptions: options.join(", "),
total: productVariation.price,
);
CartLineItem cartLineItem = CartLineItem.fromProductVariation(
quantityAmount: _quantityIndicator,
options: options,
product: _product,
productVariation: productVariation);
_itemAddToCart(cartLineItem: cartLineItem);
Navigator.of(context).pop();
@ -281,9 +267,18 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
if (_wooSignalApp.wishlistEnabled)
IconButton(
onPressed: _toggleWishList,
icon: isInFavourites
? Icon(Icons.favorite, color: Colors.red)
: Icon(Icons.favorite_border, color: Colors.black54),
),
CartIconWidget(),
],
title: StoreLogo(height: 55, showBgWhite: (Theme.of(context).brightness == Brightness.dark)),
title: StoreLogo(
height: 55,
showBgWhite: (Theme.of(context).brightness == Brightness.dark)),
centerTitle: true,
),
body: SafeArea(
@ -302,9 +297,11 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
child: Swiper(
itemBuilder: (BuildContext context, int index) =>
CachedImageWidget(
image: _product.images.length != 0 ? _product.images[index].src : getEnv("PRODUCT_PLACEHOLDER_IMAGE"),
image: _product.images.isNotEmpty
? _product.images[index].src
: getEnv("PRODUCT_PLACEHOLDER_IMAGE"),
),
itemCount: _product.images.length == 0
itemCount: _product.images.isEmpty
? 1
: _product.images.length,
viewportFraction: 0.85,
@ -375,7 +372,6 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
Container(
decoration: BoxDecoration(
color: ThemeColor.get(context).background,
// boxShadow: wsBoxShadow(),
borderRadius: BorderRadius.circular(4),
),
padding:
@ -548,22 +544,8 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
return;
}
_itemAddToCart(
cartLineItem: CartLineItem(
name: _product.name,
productId: _product.id,
quantity: _quantityIndicator,
taxStatus: _product.taxStatus,
shippingClassId: _product.shippingClassId.toString(),
subtotal: _product.price,
taxClass: _product.taxClass,
isManagedStock: _product.manageStock,
stockQuantity: _product.stockQuantity,
shippingIsTaxable: _product.shippingTaxable,
imageSrc: _product.images.length == 0
? getEnv("PRODUCT_PLACEHOLDER_IMAGE")
: _product.images.first.src,
total: _product.price,
));
cartLineItem: CartLineItem.fromProduct(
quantityAmount: _quantityIndicator, product: _product));
}
_addQuantityTapped() {
@ -591,4 +573,23 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
});
}
}
_toggleWishList() async {
String subtitleMsg;
if (isInFavourites) {
await removeWishlistProduct(product: _product);
subtitleMsg = trans("This product has been removed from your wishlist");
} else {
await saveWishlistProduct(product: _product);
subtitleMsg = trans("This product has been added to your wishlist");
}
showStatusAlert(context,
title: trans("Success"),
subtitle: subtitleMsg,
icon: Icons.favorite,
duration: 1);
isInFavourites = !isInFavourites;
setState(() {});
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -33,8 +33,8 @@ class _ProductImageViewerPageState extends NyState<ProductImageViewerPage> {
@override
void initState() {
Map<String, dynamic> imageData = widget.controller.data();
this._initialIndex = imageData['index'];
this._arrImageSrc = imageData['images'];
_initialIndex = imageData['index'];
_arrImageSrc = imageData['images'];
super.initState();
}
@ -49,11 +49,11 @@ class _ProductImageViewerPageState extends NyState<ProductImageViewerPage> {
index: _initialIndex,
itemBuilder: (BuildContext context, int index) =>
CachedImageWidget(
image: (_arrImageSrc.length == 0
image: (_arrImageSrc.isEmpty
? getEnv("PRODUCT_PLACEHOLDER_IMAGE")
: _arrImageSrc[index]),
),
itemCount: _arrImageSrc.length == 0 ? 1 : _arrImageSrc.length,
itemCount: _arrImageSrc.isEmpty ? 1 : _arrImageSrc.length,
viewportFraction: 0.9,
scale: 0.95,
),

View File

@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/cached_image_widget.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/products.dart';
class WishListPageWidget extends StatefulWidget {
@override
_WishListPageWidgetState createState() => _WishListPageWidgetState();
}
class _WishListPageWidgetState extends State<WishListPageWidget> {
List<Product> _products = [];
bool isLoading;
@override
void initState() {
super.initState();
isLoading = true;
loadProducts();
}
loadProducts() async {
List<dynamic> favouriteProducts = await getWishlistProducts();
List<int> productIds =
favouriteProducts.map((e) => e['id']).cast<int>().toList();
if (productIds.isEmpty) {
setState(() {
isLoading = false;
});
return;
}
_products = await appWooSignal((api) => api.getProducts(
include: productIds,
perPage: 100,
status: "publish",
stockStatus: "instock",
));
setState(() {
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(trans("Wishlist")),
),
body: SafeArea(
child: isLoading
? AppLoaderWidget()
: _products.isEmpty && isLoading == false
? Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.favorite,
size: 40,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 12),
),
Text(trans("No items found"),
style: Theme.of(context)
.textTheme
.headline6
.setColor(
context, (color) => color.primaryContent))
],
),
)
: ListView.separated(
padding: EdgeInsets.only(top: 10),
itemBuilder: (BuildContext context, int index) {
Product product = _products[index];
return InkWell(
onTap: () => Navigator.pushNamed(
context, "/product-detail",
arguments: product),
child: Container(
child: Row(
children: [
Container(
child: CachedImageWidget(
image: (product.images.isNotEmpty
? product.images.first.src
: getEnv("PRODUCT_PLACEHOLDER_IMAGE")),
fit: BoxFit.contain,
width: double.infinity,
),
width: MediaQuery.of(context).size.width / 4,
),
Expanded(
child: Container(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(product.name),
Text(
formatStringCurrency(
total: product.price),
),
],
),
),
),
Container(
width: 100,
alignment: Alignment.center,
child: IconButton(
icon: Icon(
Icons.favorite,
color: Colors.red,
),
onPressed: () =>
_removeFromWishlist(product),
),
)
],
),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemCount: _products.length)),
);
}
_removeFromWishlist(Product product) async {
await removeWishlistProduct(product: product);
showToastNotification(
context,
title: trans('Success'),
icon: Icons.shopping_cart,
description: trans('Item removed'),
);
_products.remove(product);
setState(() {});
}
}

View File

@ -17,20 +17,22 @@ import 'package:nylo_framework/nylo_framework.dart';
ThemeData darkTheme(BaseColorStyles darkColors) {
try {
appFont = GoogleFonts.getFont(AppHelper.instance.appConfig.themeFont ?? "Poppins");
} on Exception catch(e) {
appFont = GoogleFonts.getFont(
AppHelper.instance.appConfig.themeFont ?? "Poppins");
} on Exception catch (e) {
if (getEnv('APP_DEBUG') == true) {
NyLogger.error(e.toString());
}
}
TextTheme darkTheme =
getAppTextTheme(appFont, defaultTextTheme.merge(_darkTextTheme(darkColors)));
TextTheme darkTheme = getAppTextTheme(
appFont, defaultTextTheme.merge(_darkTextTheme(darkColors)));
return ThemeData(
primaryColor: darkColors.primaryContent,
backgroundColor: darkColors.background,
colorScheme: ColorScheme.dark(),
primaryColorDark: darkColors.primaryContent,
brightness: Brightness.dark,
focusColor: darkColors.primaryContent,
scaffoldBackgroundColor: darkColors.background,
appBarTheme: AppBarTheme(
@ -55,13 +57,13 @@ ThemeData darkTheme(BaseColorStyles darkColors) {
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: darkColors.bottomTabBarBackground,
unselectedIconTheme:
IconThemeData(color: darkColors.bottomTabBarIconUnselected),
IconThemeData(color: darkColors.bottomTabBarIconUnselected),
selectedIconTheme:
IconThemeData(color: darkColors.bottomTabBarIconSelected),
IconThemeData(color: darkColors.bottomTabBarIconSelected),
unselectedLabelStyle:
TextStyle(color: darkColors.bottomTabBarLabelUnselected),
TextStyle(color: darkColors.bottomTabBarLabelUnselected),
selectedLabelStyle:
TextStyle(color: darkColors.bottomTabBarLabelSelected),
TextStyle(color: darkColors.bottomTabBarLabelSelected),
selectedItemColor: darkColors.bottomTabBarLabelSelected,
),
textTheme: darkTheme,

View File

@ -17,15 +17,16 @@ import 'package:nylo_framework/nylo_framework.dart';
ThemeData lightTheme(BaseColorStyles lightColors) {
try {
appFont = GoogleFonts.getFont(AppHelper.instance.appConfig.themeFont ?? "Poppins");
} on Exception catch(e) {
appFont = GoogleFonts.getFont(
AppHelper.instance.appConfig.themeFont ?? "Poppins");
} on Exception catch (e) {
if (getEnv('APP_DEBUG') == true) {
NyLogger.error(e.toString());
}
}
TextTheme lightTheme =
getAppTextTheme(appFont, defaultTextTheme.merge(_lightTextTheme(lightColors)));
TextTheme lightTheme = getAppTextTheme(
appFont, defaultTextTheme.merge(_lightTextTheme(lightColors)));
return ThemeData(
primaryColor: lightColors.primaryContent,
@ -58,13 +59,13 @@ ThemeData lightTheme(BaseColorStyles lightColors) {
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: lightColors.bottomTabBarBackground,
unselectedIconTheme:
IconThemeData(color: lightColors.bottomTabBarIconUnselected),
IconThemeData(color: lightColors.bottomTabBarIconUnselected),
selectedIconTheme:
IconThemeData(color: lightColors.bottomTabBarIconSelected),
IconThemeData(color: lightColors.bottomTabBarIconSelected),
unselectedLabelStyle:
TextStyle(color: lightColors.bottomTabBarLabelUnselected),
TextStyle(color: lightColors.bottomTabBarLabelUnselected),
selectedLabelStyle:
TextStyle(color: lightColors.bottomTabBarLabelSelected),
TextStyle(color: lightColors.bottomTabBarLabelSelected),
selectedItemColor: lightColors.bottomTabBarLabelSelected,
),
textTheme: lightTheme,

View File

@ -33,4 +33,4 @@ abstract class BaseColorStyles {
Color get bottomTabBarLabelSelected;
Color get inputPrimaryContent;
}
}

View File

@ -10,33 +10,55 @@ import 'package:flutter_app/resources/themes/styles/base_styles.dart';
class DarkThemeColors implements BaseColorStyles {
// general
Color get background => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['background']));
@override
Color get background => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['background']));
@override
Color get backgroundContainer => const Color(0xFF4a4a4a);
Color get primaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['primary_text']));
@override
Color get primaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['primary_text']));
@override
Color get primaryAccent => const Color(0xFF818181);
@override
Color get surfaceBackground => Color(0xFF818181);
@override
Color get surfaceContent => Colors.black;
// app bar
Color get appBarBackground => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['app_bar_background']));
Color get appBarPrimaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['app_bar_text']));
@override
Color get appBarBackground => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['app_bar_background']));
@override
Color get appBarPrimaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['app_bar_text']));
@override
Color get inputPrimaryContent => Colors.white;
// buttons
Color get buttonBackground => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['button_background']));
Color get buttonPrimaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['dark']['button_text']));
@override
Color get buttonBackground => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['button_background']));
@override
Color get buttonPrimaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['dark']['button_text']));
// bottom tab bar
@override
Color get bottomTabBarBackground => const Color(0xFF232c33);
// bottom tab bar - icons
@override
Color get bottomTabBarIconSelected => Colors.white70;
@override
Color get bottomTabBarIconUnselected => Colors.white60;
// bottom tab bar - label
@override
Color get bottomTabBarLabelUnselected => Colors.white54;
@override
Color get bottomTabBarLabelSelected => Colors.white;
}
}

View File

@ -11,32 +11,54 @@ import 'package:flutter_app/resources/themes/styles/base_styles.dart';
class LightThemeColors implements BaseColorStyles {
// general
Color get background => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['background']));
@override
Color get background => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['background']));
@override
Color get backgroundContainer => Colors.white;
Color get primaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['primary_text']));
@override
Color get primaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['primary_text']));
@override
Color get primaryAccent => const Color(0xFF87c694);
@override
Color get surfaceBackground => Colors.white;
@override
Color get surfaceContent => Colors.black;
// app bar
Color get appBarBackground => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['app_bar_background']));
Color get appBarPrimaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['app_bar_text']));
@override
Color get appBarBackground => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['app_bar_background']));
@override
Color get appBarPrimaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['app_bar_text']));
@override
Color get inputPrimaryContent => Colors.black;
// buttons
Color get buttonBackground => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['button_background']));
Color get buttonPrimaryContent => Color(int.parse(AppHelper.instance.appConfig.themeColors['light']['button_text']));
@override
Color get buttonBackground => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['button_background']));
@override
Color get buttonPrimaryContent => Color(int.parse(
AppHelper.instance.appConfig.themeColors['light']['button_text']));
// bottom tab bar
@override
Color get bottomTabBarBackground => Colors.white;
// bottom tab bar - icons
@override
Color get bottomTabBarIconSelected => Colors.blue;
@override
Color get bottomTabBarIconUnselected => Colors.black54;
// bottom tab bar - label
@override
Color get bottomTabBarLabelUnselected => Colors.black45;
@override
Color get bottomTabBarLabelSelected => Colors.black;
}

View File

@ -1,14 +1,13 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:hexcolor/hexcolor.dart';
@ -20,7 +19,6 @@ class AppLoaderWidget extends StatelessWidget {
Widget build(BuildContext context) {
bool isDark = (Theme.of(context).brightness == Brightness.dark);
return SpinKitDoubleBounce(
color: HexColor(!isDark ? "#424242" : "#c7c7c7")
);
color: HexColor(!isDark ? "#424242" : "#c7c7c7"));
}
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -29,8 +29,7 @@ class AppVersionWidget extends StatelessWidget {
case ConnectionState.done:
if (snapshot.hasError) return Text("");
return Padding(
child: Text(
"${trans("Version")}: ${snapshot.data.version}",
child: Text("${trans("Version")}: ${snapshot.data.version}",
style: Theme.of(context)
.textTheme
.bodyText2

View File

@ -1,13 +1,14 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:hexcolor/hexcolor.dart';
@ -27,11 +28,11 @@ class PrimaryButton extends StatelessWidget {
key: key,
title: title,
action: action,
textStyle: Theme.of(context)
.textTheme
.button
.copyWith(fontSize: 16, fontWeight: FontWeight.bold, color: ThemeColor.get(context).buttonPrimaryContent),
bgColor: ThemeColor.get(context).buttonBackground,
textStyle: Theme.of(context).textTheme.button.copyWith(
fontSize: 16,
fontWeight: FontWeight.bold,
color: ThemeColor.get(context).buttonPrimaryContent),
bgColor: ThemeColor.get(context).buttonBackground,
);
}
@ -109,14 +110,14 @@ class WooSignalButton extends StatelessWidget {
elevation: 0,
primary: bgColor,
shadowColor: Colors.transparent),
child: Text(
child: AutoSizeText(
title,
style: textStyle,
maxLines: (screenWidth >= 385 ? 2 : 1),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
onPressed: action ?? null,
onPressed: action,
),
);
}

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -19,10 +19,9 @@ class CachedImageWidget extends StatelessWidget {
this.width = 70,
this.placeholder = const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
backgroundColor: Colors.black12,
color: Colors.black54
),
strokeWidth: 2,
backgroundColor: Colors.black12,
color: Colors.black54),
),
this.fit = BoxFit.contain,
}) : super(key: key);
@ -37,7 +36,7 @@ class CachedImageWidget extends StatelessWidget {
Widget build(BuildContext context) => CachedNetworkImage(
imageUrl: image,
placeholder: (context, url) => placeholder,
errorWidget: (context, url, error) => new Icon(Icons.error),
errorWidget: (context, url, error) => Icon(Icons.error),
height: height,
width: width,
alignment: Alignment.center,

View File

@ -1,7 +1,7 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2021, WooSignal Ltd. All rights reserved.
// 2022, WooSignal Ltd. All rights reserved.
//
// Unless required by applicable law or agreed to in writing, software
@ -48,7 +48,7 @@ class _CartIconWidgetState extends State<CartIconWidget> {
List<int> cartItems =
snapshot.data.map((e) => e.quantity).toList();
String cartValue = "0";
if (cartItems.length > 0) {
if (cartItems.isNotEmpty) {
cartValue = cartItems
.reduce((value, element) => value + element)
.toString();

View File

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class CategorySubcategoryScrollWidget extends StatefulWidget {
@override
_CategorySubcategoryScrollWidgetState createState() =>
_CategorySubcategoryScrollWidgetState();
}
class _CategorySubcategoryScrollWidgetState
extends State<CategorySubcategoryScrollWidget> {
@override
Widget build(BuildContext context) {
return Container();
}
}

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
class CheckoutCouponAmountWidget extends StatelessWidget {
const CheckoutCouponAmountWidget({Key key, @required this.checkoutSession})
: super(key: key);
final CheckoutSession checkoutSession;
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: Cart.getInstance.couponDiscountAmount(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return AppLoaderWidget();
default:
if (snapshot.hasError) {
return Text("");
} else {
if (checkoutSession.coupon == null) {
return Container();
}
return Padding(
child: CheckoutMetaLine(
title: "${trans('Coupon')}: ${checkoutSession.coupon.code}",
amount: "-" + formatStringCurrency(total: snapshot.data),
),
padding: EdgeInsets.only(bottom: 0, top: 0),
);
}
}
},
);
}
}

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
class CheckoutPaymentTypeWidget extends StatelessWidget {
const CheckoutPaymentTypeWidget(
{Key key,
@required this.context,
@required this.checkoutSession,
this.resetState})
: super(key: key);
final CheckoutSession checkoutSession;
final BuildContext context;
final Function resetState;
@override
Widget build(BuildContext context) {
bool hasPaymentType = checkoutSession.paymentType != null;
return CheckoutRowLine(
heading: trans(hasPaymentType ? "Payment method" : "Pay with"),
leadImage: hasPaymentType
? Container(
color: Colors.white,
child: Image.asset(
getImageAsset(checkoutSession.paymentType.assetImage),
width: 70,
),
)
: Icon(Icons.payment),
leadTitle: hasPaymentType
? checkoutSession.paymentType.desc
: trans("Select a payment method"),
action: _actionPayWith,
showBorderBottom: true,
);
}
_actionPayWith() {
Navigator.pushNamed(context, "/checkout-payment-type")
.then((value) => resetState());
}
}

View File

@ -24,50 +24,51 @@ class PayPalCheckout extends StatefulWidget {
class WebViewState extends NyState<PayPalCheckout> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
Completer<WebViewController>();
String payerId = '';
int intCount = 0;
StreamSubscription<String> _onUrlChanged;
WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
String formCheckoutShippingAddress;
setCheckoutShippingAddress(CustomerAddress customerAddress) {
String tmp = "";
if (customerAddress.firstName != null) {
tmp +=
'<input type="hidden" name="first_name" value="${customerAddress.firstName.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="first_name" value="${customerAddress.firstName.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.lastName != null) {
tmp +=
'<input type="hidden" name="last_name" value="${customerAddress.lastName.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="last_name" value="${customerAddress.lastName.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.addressLine != null) {
tmp +=
'<input type="hidden" name="address1" value="${customerAddress.addressLine.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="address1" value="${customerAddress.addressLine.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.city != null) {
tmp +=
'<input type="hidden" name="city" value="${customerAddress.city.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="city" value="${customerAddress.city.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.customerCountry.hasState() &&
customerAddress.customerCountry.state.name != null) {
tmp +=
'<input type="hidden" name="state" value="${customerAddress.customerCountry.state.name.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="state" value="${customerAddress.customerCountry.state.name.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.postalCode != null) {
tmp +=
'<input type="hidden" name="zip" value="${customerAddress.postalCode.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="zip" value="${customerAddress.postalCode.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
if (customerAddress.customerCountry.countryCode != null) {
tmp +=
'<input type="hidden" name="country" value="${customerAddress.customerCountry.countryCode.replaceAll(new RegExp(r'[^\d\w\s,\-+]+'),'')}">\n';
'<input type="hidden" name="country" value="${customerAddress.customerCountry.countryCode.replaceAll(RegExp(r'[^\d\w\s,\-+]+'), '')}">\n';
}
formCheckoutShippingAddress = tmp;
}
String getPayPalItemName() {
return truncateString(widget.description.replaceAll(new RegExp(r'[^\w\s]+'),''), 124);
return truncateString(
widget.description.replaceAll(RegExp(r'[^\w\s]+'), ''), 124);
}
String getPayPalPaymentType() {
@ -75,7 +76,8 @@ class WebViewState extends NyState<PayPalCheckout> {
}
String getPayPalUrl() {
bool liveMode = envVal('PAYPAL_LIVE_MODE', defaultValue: _wooSignalApp.paypalLiveMode);
bool liveMode =
envVal('PAYPAL_LIVE_MODE', defaultValue: _wooSignalApp.paypalLiveMode);
return liveMode == true
? "https://www.paypal.com/cgi-bin/webscr"
: "https://www.sandbox.paypal.com/cgi-bin/webscr";
@ -100,8 +102,10 @@ class WebViewState extends NyState<PayPalCheckout> {
String _loadHTML() {
final String strProcessingPayment = trans("Processing Payment");
final String strPleaseWait = trans("Please wait, your order is being processed and you will be redirected to the PayPal website.");
final String strRedirectMessage = trans("If you are not automatically redirected to PayPal within 5 seconds");
final String strPleaseWait = trans(
"Please wait, your order is being processed and you will be redirected to the PayPal website.");
final String strRedirectMessage = trans(
"If you are not automatically redirected to PayPal within 5 seconds");
return '''
<html><head><title>$strProcessingPayment...</title></head>
@ -125,7 +129,8 @@ $formCheckoutShippingAddress
<center><br><br>$strRedirectMessage...<br><br>
<input type="submit" value="Click Here"></center>
</form></body></html>
'''.toString();
'''
.toString();
}
@override
@ -134,18 +139,17 @@ $formCheckoutShippingAddress
resizeToAvoidBottomInset: false,
body: SafeArea(
child: WebView(
initialUrl: Uri.dataFromString(_loadHTML(), mimeType: 'text/html').toString(),
initialUrl:
Uri.dataFromString(_loadHTML(), mimeType: 'text/html').toString(),
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
onProgress: (int progress) {
},
onProgress: (int progress) {},
navigationDelegate: (NavigationRequest request) {
return NavigationDecision.navigate;
},
onPageStarted: (String url) {
},
onPageStarted: (String url) {},
onPageFinished: (String url) {
if (intCount > 0) {
url = url.replaceAll("~", "_");
@ -157,7 +161,10 @@ $formCheckoutShippingAddress
setState(() {
payerId = uri.queryParameters['PayerID'];
});
Navigator.pop(context, {"status": payerId == null ? "cancelled" : "success", "payerId": payerId});
Navigator.pop(context, {
"status": payerId == null ? "cancelled" : "success",
"payerId": payerId
});
} else if (url.contains("payment_failure")) {
Navigator.pop(context, {"status": "cancelled"});
}

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:nylo_framework/nylo_framework.dart';
class CheckoutSelectCouponWidget extends StatelessWidget {
const CheckoutSelectCouponWidget(
{Key key,
@required this.context,
@required this.checkoutSession,
@required this.resetState})
: super(key: key);
final CheckoutSession checkoutSession;
final BuildContext context;
final Function resetState;
@override
Widget build(BuildContext context) {
bool hasCoupon = checkoutSession.coupon != null;
return InkWell(
onTap: _actionCoupon,
child: Container(
padding: EdgeInsets.symmetric(vertical: 5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
hasCoupon == true
? IconButton(
padding: EdgeInsets.symmetric(vertical: 3),
onPressed: _clearCoupon,
icon: Icon(
Icons.close,
size: 19,
))
: null,
Text(
hasCoupon
? "Coupon Applied: " + checkoutSession.coupon.code
: trans('Apply Coupon'),
style: Theme.of(context).textTheme.subtitle2,
),
].where((element) => element != null).toList(),
),
),
);
}
_clearCoupon() {
CheckoutSession.getInstance.coupon = null;
resetState();
}
_actionCoupon() {
if (checkoutSession.billingDetails.billingAddress == null) {
showToastNotification(
context,
title: trans("Oops"),
description:
trans("Please select add your billing/shipping address to proceed"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.local_shipping,
);
return;
}
if (checkoutSession.billingDetails.billingAddress.hasMissingFields()) {
showToastNotification(
context,
title: trans("Oops"),
description: trans("Your billing/shipping details are incomplete"),
style: ToastNotificationStyleType.WARNING,
icon: Icons.local_shipping,
);
return;
}
Navigator.pushNamed(context, "/checkout-coupons")
.then((value) => resetState());
}
}

View File

@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/app/models/customer_address.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
class CheckoutShippingTypeWidget extends StatelessWidget {
const CheckoutShippingTypeWidget(
{Key key,
@required this.context,
@required this.wooSignalApp,
@required this.checkoutSession,
this.resetState})
: super(key: key);
final CheckoutSession checkoutSession;
final BuildContext context;
final Function resetState;
final WooSignalApp wooSignalApp;
@override
Widget build(BuildContext context) {
bool hasDisableShipping = wooSignalApp.disableShipping == 1;
if (hasDisableShipping == true) {
return null;
}
bool hasSelectedShippingType = checkoutSession.shippingType != null;
return CheckoutRowLine(
heading: trans(
hasSelectedShippingType ? "Shipping selected" : "Select shipping"),
leadImage: Icon(Icons.local_shipping),
leadTitle: hasSelectedShippingType
? checkoutSession.shippingType.getTitle()
: trans("Select a shipping option"),
action: _actionSelectShipping,
showBorderBottom: false,
);
}
_actionSelectShipping() {
CustomerAddress shippingAddress =
checkoutSession.billingDetails.shippingAddress;
if (shippingAddress == null || shippingAddress.customerCountry == null) {
showToastNotification(context,
title: trans("Oops"),
description: trans("Add your shipping details first"),
icon: Icons.local_shipping);
return;
}
Navigator.pushNamed(context, "/checkout-shipping-type")
.then((value) => resetState());
}
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
class CheckoutStoreHeadingWidget extends StatelessWidget {
const CheckoutStoreHeadingWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
boxShadow: (Theme.of(context).brightness == Brightness.light)
? wsBoxShadow(blurRadius: 10)
: null,
color: Colors.transparent,
),
padding: EdgeInsets.all(2),
margin: EdgeInsets.only(top: 16),
child: ClipRRect(
child: StoreLogo(height: 65),
borderRadius: BorderRadius.circular(8),
));
}
}

View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/app/models/checkout_session.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:nylo_framework/nylo_framework.dart';
class CheckoutUserDetailsWidget extends StatelessWidget {
const CheckoutUserDetailsWidget(
{Key key,
@required this.context,
@required this.checkoutSession,
this.resetState})
: super(key: key);
final CheckoutSession checkoutSession;
final BuildContext context;
final Function resetState;
@override
Widget build(BuildContext context) {
bool hasUserCheckoutInfo = (checkoutSession.billingDetails != null &&
checkoutSession.billingDetails.billingAddress != null);
return CheckoutRowLine(
heading: trans("Billing/shipping details"),
leadImage: Icon(Icons.home),
leadTitle: hasUserCheckoutInfo
? (checkoutSession.billingDetails == null ||
checkoutSession.billingDetails.billingAddress
.hasMissingFields()
? trans("Billing address is incomplete")
: checkoutSession.billingDetails.billingAddress.addressFull())
: trans("Add billing & shipping details"),
action: _actionCheckoutDetails,
showBorderBottom: true,
);
}
_actionCheckoutDetails() {
Navigator.pushNamed(context, "/checkout-details").then((e) {
resetState();
// setState(() {
// _showFullLoader = true;
// });
// _getTaxes();
});
}
}

View File

@ -0,0 +1,186 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
import 'package:flutter_app/resources/widgets/buttons.dart';
import 'package:flutter_app/resources/widgets/cached_image_widget.dart';
import 'package:flutter_app/resources/widgets/home_drawer_widget.dart';
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:woosignal/models/response/product_category.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
import 'package:woosignal/models/response/products.dart';
class CompoHomeWidget extends StatefulWidget {
CompoHomeWidget({Key key, @required this.wooSignalApp}) : super(key: key);
final WooSignalApp wooSignalApp;
@override
_CompoHomeWidgetState createState() => _CompoHomeWidgetState();
}
class _CompoHomeWidgetState extends State<CompoHomeWidget> {
@override
void initState() {
super.initState();
_loadHome();
}
_loadHome() async {
categories = await appWooSignal((api) =>
api.getProductCategories(parent: 0, perPage: 20, hideEmpty: true));
categories.sort((category1, category2) =>
category1.menuOrder.compareTo(category2.menuOrder));
for (var category in categories) {
List<Product> products = await appWooSignal((api) => api.getProducts(
perPage: 10,
category: category.id.toString(),
status: "publish",
stockStatus: "instock"));
if (products.isNotEmpty) {
categoryAndProducts.addAll({category: products});
setState(() {});
}
}
}
List<ProductCategory> categories = [];
Map<ProductCategory, List<Product>> categoryAndProducts = {};
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
List<String> bannerImages = widget.wooSignalApp.bannerImages;
return Scaffold(
drawer: HomeDrawerWidget(wooSignalApp: widget.wooSignalApp),
appBar: AppBar(
centerTitle: true,
title: StoreLogo(),
elevation: 0,
),
body: SafeArea(
child: categoryAndProducts.isEmpty
? AppLoaderWidget()
: ListView(
shrinkWrap: true,
children: [
if (bannerImages.isNotEmpty)
Container(
child: Swiper(
itemBuilder: (BuildContext context, int index) {
return CachedImageWidget(
image: bannerImages[index],
fit: BoxFit.cover,
);
},
itemCount: bannerImages.length,
viewportFraction: 0.8,
scale: 0.9,
),
height: size.height / 2.5,
),
...categoryAndProducts.entries.map((catProds) {
double containerHeight = size.height / 1.1;
bool hasImage = catProds.key.image != null;
if (hasImage == false) {
containerHeight = (containerHeight / 2);
}
return Container(
height: containerHeight,
width: size.width,
margin: EdgeInsets.only(top: 10),
child: Column(
children: [
hasImage
? InkWell(
child: CachedImageWidget(
image: catProds.key.image.src,
height: containerHeight / 2,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
),
onTap: () => _showCategory(catProds.key))
: null,
ConstrainedBox(
constraints: BoxConstraints(
minHeight: 50,
minWidth: double.infinity,
maxHeight: 80.0,
maxWidth: double.infinity,
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
catProds.key.name,
style: Theme.of(context)
.textTheme
.subtitle1
.copyWith(
fontWeight: FontWeight.bold,
fontSize: 22),
),
),
Flexible(
child: Container(
width: size.width / 4,
child: LinkButton(
title: trans("View All"),
action: () =>
_showCategory(catProds.key),
),
),
),
],
),
),
),
Container(
height: hasImage
? (containerHeight / 2) / 1.2
: containerHeight / 1.2,
padding: EdgeInsets.symmetric(horizontal: 8),
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: false,
itemBuilder: (cxt, i) {
Product product = catProds.value[i];
return Container(
height: MediaQuery.of(cxt).size.height,
width: size.width / 2.5,
child: ProductItemContainer(
product: product, onTap: _showProduct),
);
},
itemCount: catProds.value.length,
),
)
].where((e) => e != null).toList(),
),
);
}),
],
),
),
);
}
_showCategory(ProductCategory productCategory) =>
Navigator.pushNamed(context, "/browse-category",
arguments: productCategory);
_showProduct(Product product) =>
Navigator.pushNamed(context, "/product-detail", arguments: product);
}

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