001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.petstore.console.pages; 018 019import java.util.*; 020 021import org.apache.juneau.petstore.dto.*; 022import org.apache.juneau.petstore.service.*; 023import org.apache.juneau.console.freemarker.*; 024import org.apache.juneau.console.views.*; 025import org.apache.juneau.console.views.RowAction.*; 026import org.apache.juneau.console.views.TableSpec.*; 027import org.apache.juneau.console.widgets.*; 028 029/** 030 * Builder twin of the Store page (P1, {@code store.ftlh}). 031 * 032 * <h5 class='section'>Example:</h5> 033 * <p class='bjava'> 034 * <jk>return</jk> <jsm>twin</jsm>(<jv>req</jv>, StorePageBuilders.<jsm>store</jsm>(QuickStats.<jsm>of</jsm>(store()))); 035 * </p> 036 */ 037@SuppressWarnings({ 038 "java:S1192" // Page-definition ids and labels repeat as inline literals by design. 039}) 040public final class StorePageBuilders { 041 042 private StorePageBuilders() {} 043 044 /** 045 * The Store page's quick-stat tiles, shared by both render paths. 046 * 047 * <h5 class='section'>Example:</h5> 048 * <p class='bjava'> 049 * <jk>var</jk> <jv>qs</jv> = QuickStats.<jsm>of</jsm>(<jv>store</jv>); 050 * </p> 051 * 052 * @param available Available pets. 053 * @param pending Pending pets. 054 * @param sold Sold pets. 055 * @param ordersPlaced Placed orders. 056 * @param ordersApproved Approved orders. 057 * @param ordersDelivered Delivered orders. 058 * @param petTotal All pets, the inventory meter's maximum. 059 */ 060 public record QuickStats(long available, long pending, long sold, long ordersPlaced, long ordersApproved, long ordersDelivered, long petTotal) { 061 062 /** 063 * Counts the tiles from the store. 064 * 065 * @param store The store. 066 * @return The tiles. 067 */ 068 public static QuickStats of(PetStore store) { 069 var pets = store.getPets(); 070 var orders = store.getOrders(); 071 return new QuickStats(count(pets, PetStatus.AVAILABLE), count(pets, PetStatus.PENDING), count(pets, PetStatus.SOLD), 072 count(orders, OrderStatus.PLACED), count(orders, OrderStatus.APPROVED), count(orders, OrderStatus.DELIVERED), pets.size()); 073 } 074 075 private static long count(Collection<Pet> pets, PetStatus status) { 076 return pets.stream().filter(x -> x.getStatus() == status).count(); 077 } 078 079 private static long count(Collection<Order> orders, OrderStatus status) { 080 return orders.stream().filter(x -> x.getStatus() == status).count(); 081 } 082 } 083 084 /** The store strip: three status tiles, an order breakdown, and an inventory meter (a tile when there are no pets). */ 085 private static QuickStat[] storeQuickStats(QuickStats qs) { 086 var inventory = qs.petTotal() > 0 087 ? QuickStat.meter("inventory", "Inventory", qs.available(), qs.petTotal()).text(qs.available() + " of " + qs.petTotal()) 088 : QuickStat.of("inventory", "Inventory", "0 of 0"); 089 return new QuickStat[] { 090 QuickStat.of("available", "Available", qs.available()), 091 QuickStat.of("pending", "Pending", qs.pending()), 092 QuickStat.of("sold", "Sold", qs.sold()), 093 QuickStat.segmented("orders", "Orders").segment("Placed", qs.ordersPlaced()) 094 .segment("Approved", qs.ordersApproved(), StatusTone.INFO).segment("Delivered", qs.ordersDelivered(), StatusTone.SUCCESS), 095 inventory 096 }; 097 } 098 099 /** 100 * P1 Store. 101 * 102 * @param qs The quick-stat tiles. 103 * @return The page spec. 104 */ 105 public static PageSpec store(QuickStats qs) { 106 return PageSpec.create().tab("store").toolkit("views").init("/console/dev/cards/petstore-cards.js") 107 .html("welcome", "Welcome to the petstore", """ 108 <div class="jc-prose"><p>Browse pets, place orders and review what changed. Every page here is built from the same 109 Juneau console pieces your own app can use.</p> 110 <p data-slot="welcome-note">Signed in as a guest.</p></div>""") 111 .table(TableSpec.create("stats").rowType("Order").dataMode(DataMode.CLIENT).dataUrl("/console/store/recent-orders") 112 .columns( 113 Column.create("id").label("Order").href("/console/pets/{petId}/orders/{id}"), 114 Column.create("petId").label("Pet").href("/console/pets/{petId}/summary"), 115 Column.create("shipDate").label("Ship date").render("date"), 116 Column.create("status").label("Status").render("pill")) 117 .quickStats("store", storeQuickStats(qs)), "Recent orders") 118 .card(CardSpec.of("kpi", "revenue").title("Revenue") 119 .body("label", "Revenue today").body("dataUrl", "/console/store/kpi/revenue")) 120 .card(ChartDef.line("orders-trend").title("Orders, last 30 days").src("/console/store/stats/daily") 121 .x("day", null, "Day").series("placed", "Placed").series("delivered", "Delivered", "ok") 122 .onClickHref("/console/orders?state=day({day})").toCardSpec()) 123 .card(ChartDef.donut("by-species").title("Pets by species").src("/console/store/stats/species") 124 .label("species").value("count").center("total").toCardSpec()) 125 .card(ChartDef.bar("by-status").title("Pets by status").src("/console/store/stats/status") 126 .x("status").series("count", "Pets").toCardSpec()) 127 .card(ChartDef.treemap("pets-map").title("Pets by species and status").src("/console/store/stats/pets") 128 .groupBy("species", "status").label("name") 129 .colorBy("status", ChartDef.legendItem("AVAILABLE", "Available", "ok"), 130 ChartDef.legendItem("PENDING", "Pending", "warning"), 131 ChartDef.legendItem("SOLD", "Sold", null)) 132 .tileFields("id", "name", "species", "status") 133 .onClickAction(PetsPageBuilders.editPetAction(OnSuccess.REDRAW)).toCardSpec()) 134 .htmlSrc("hours", "Store hours", "/console/store/fragments/hours"); 135 } 136}