DEV Community

Lin
Lin

Posted on

Imitation Hema - Choose Coupon (59)

Technical Stack
Appgallery connect

Development Preparation
In the previous section, we implemented coupon query on the order confirmation page, but lacked other coupon-related logic. Our goal is to use corresponding coupons during order settlement. Now, we need to enable coupon selection on the order confirmation page, presenting it in a pop-up window for user convenience.

Functional Analysis
To display coupons in a pop-up window:

Create a custom pop-up dialog.
Query all coupon data to show both usable and unusable coupons.
Inform users of usable coupon amounts and the remaining amount needed for unusable coupons to be valid.
Allow clicking on usable coupons to modify the settlement amount and display the selected coupon amount.

Code Implementation
First, create the pop-up window with two-way binding for coupon amount:

@Preview
@CustomDialog
export struct CouponCheckDialog {
@State user: User | null = null
@State couponList: CouponMall[] = []
controller: CustomDialogController;
@State lessThanLimit: CouponMall[] = []
@State greaterOrEqualLimit: CouponMall[] = []
@prop price: number
@link couponPrice: number

async aboutToAppear(): Promise {
const value = await StorageUtils.getAll('user');
if (value != "") {
this.user = JSON.parse(value)
}
let databaseZone = cloudDatabase.zone('default');
let condition = new cloudDatabase.DatabaseQuery(coupon_mall);
condition.equalTo("user_id", this.user?.user_id)
let listData = await databaseZone.query(condition);
let json = JSON.stringify(listData)
let data: CouponMall[] = JSON.parse(json)
this.couponList = data

this.splitCouponsByLimit(data, this.price)
Enter fullscreen mode Exit fullscreen mode

}

@builder
Header(type: number) {
Text(type == 0 ? "Usable Coupons" : "Unusable Coupons")
.width('100%')
.padding(10)
.textAlign(TextAlign.Start)
.fontColor(Color.Black)
.fontSize(16)
}

@builder
Item(item: CouponMall) {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column() {
Text("¥" + item.price)
.fontSize(30)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
Text("Valid for orders over ¥" + item.limit_amount)
.fontColor(Color.Red)
.fontWeight(FontWeight.Bold)
.fontSize(12)
}

    Column({ space: 10 }) {
      Text(item.type_str)
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Bold)
        .fontSize(16)

      Text(item.txt)
        .fontColor(Color.Grey)
        .fontSize(12)
    }

    Blank()
    Text("Ready to Use")
      .width(80)
      .height(80)
      .borderRadius(40)
      .fontSize(14)
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
      .backgroundColor(Color.Red)
  }
  .width('100%')
  .justifyContent(FlexAlign.SpaceBetween)

  Divider().width('100%').height(0.8)
    .color("#e6e6e6")
  Text("Valid from " + item.start_time + " to " + item.end_time)
    .fontSize(12)
    .fontColor(Color.Grey)
}
.margin({ top: 10 })
.padding(10)
.backgroundColor(Color.White)
.borderRadius(10)
.onClick(() => {
  this.couponPrice = item.price
  this.controller.close()
})
Enter fullscreen mode Exit fullscreen mode

}

@builder
NotItem(item: CouponMall) {
Column({ space: 10 }) {
Row({ space: 10 }) {
Column() {
Text("¥" + item.price)
.fontSize(30)
.fontColor(Color.Grey)
.fontWeight(FontWeight.Bold)
Text("Valid for orders over ¥" + item.limit_amount)
.fontColor(Color.Grey)
.fontWeight(FontWeight.Bold)
.fontSize(12)
}

    Column({ space: 10 }) {
      Text(item.type_str)
        .fontColor(Color.Grey)
        .fontWeight(FontWeight.Bold)
        .fontSize(16)

      Text(item.txt)
        .fontColor(Color.Grey)
        .fontSize(12)
    }

    Blank()
    Text("Unusable")
      .width(80)
      .height(80)
      .borderRadius(40)
      .fontSize(14)
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
      .backgroundColor(Color.Grey)
  }
  .width('100%')
  .justifyContent(FlexAlign.SpaceBetween)

  Divider().width('100%').height(0.8)
    .color("#e6e6e6")

  Text("Valid from " + item.start_time + " to " + item.end_time)
    .fontSize(12)
    .fontColor(Color.Grey)
  Text() {
    Span("Reason for unavailability: ")
      .fontColor(Color.Red)
      .fontSize(12)
    Span("Order amount needs ¥")
      .fontColor(Color.Gray)
      .fontSize(12)
    Span("" + (item.limit_amount - this.price))
      .fontColor(Color.Red)
      .fontSize(12)
    Span(" more to meet the threshold")
      .fontColor(Color.Gray)
      .fontSize(12)
  }
  .backgroundColor("#fffacfcf")
  .padding(5)
  .borderRadius(5)
}
.margin({ top: 10 })
.padding(10)
.backgroundColor(Color.White)
.borderRadius(10)
.onClick(() => {
  showToast("Coupon is not usable")
})
Enter fullscreen mode Exit fullscreen mode

}

build() {
Column() {
Text("Select Coupon")
.fontSize(18)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Center)
.padding(10)
.backgroundColor(Color.White)

  List({ space: 10 }) {
    ListItemGroup({ header: this.Header(0) }) {
      ForEach(this.lessThanLimit, (item: CouponMall, index: number) => {
        ListItem() {
          this.Item(item)
        }
      })
    }
    ListItemGroup({ header: this.Header(1) }) {
      ForEach(this.greaterOrEqualLimit, (item: CouponMall, index: number) => {
        ListItem() {
          this.NotItem(item)
        }
      })
    }
  }
  .padding(10)

  Button("Confirm Selection", { type: ButtonType.Normal })
    .width('90%')
    .height(50)
    .backgroundColor(Color.Red)
    .fontColor(Color.White)
    .borderRadius(25)
    .margin({ top: 20, bottom: 30 })
    .onClick(() => {
      this.controller.close()
    })
}
.width('100%')
.backgroundColor(Color.White)
Enter fullscreen mode Exit fullscreen mode

}

splitCouponsByLimit(coupons: CouponMall[], price: number) {
const lessThanLimit = coupons.filter(coupon =>
coupon.limit_amount <= price
);
this.lessThanLimit = lessThanLimit;

const greaterOrEqualLimit = coupons.filter(coupon =>
  coupon.limit_amount > price
);
this.greaterOrEqualLimit = greaterOrEqualLimit;
Enter fullscreen mode Exit fullscreen mode

}
}

Reference the pop-up in the order confirmation page:

couponController: CustomDialogController | null = new CustomDialogController({
builder: CouponCheckDialog({
couponPrice: this.couponPrice,
price: this.price()
}),
alignment: DialogAlignment.Bottom,
customStyle: true
});

Modify the coupon display component logic:

Row() {
Text("Coupons")
.fontSize(14)
.fontColor(Color.Black)

if (this.getCoupon() > 0) {
if (this.couponPrice > 0) {
Text("Selected: -¥" + this.couponPrice + " >")
.fontSize(14)
.fontColor(Color.Red)
.onClick(() => {
this.couponController?.open()
})
} else {
Text(this.getCoupon() + " available >")
.fontSize(14)
.fontColor(Color.Red)
.onClick(() => {
this.couponController?.open()
})
}
} else {
Text("No available coupons")
.fontSize(14)
.fontColor(Color.Black)
}
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)

Top comments (0)