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.function.*;
020
021import org.apache.juneau.commons.beanquery.*;
022import org.apache.juneau.console.views.*;
023import org.apache.juneau.console.views.TableSpec.*;
024import org.apache.juneau.petstore.console.users.UsersRest.*;
025import org.apache.juneau.petstore.dto.*;
026
027/**
028 * The expandable row details of the console tables, shared by the page builders (which only need the declaration) and
029 * the resources that serve them (which pass a real lookup).
030 *
031 * <p>
032 * A bean-backed detail lists its fields explicitly, so the resource that serves it never depends on having been
033 * attached to a table.  Each field list equals the columns of the table it opens from, hidden ones included.
034 *
035 * <h2 class='section'>Example:</h2>
036 * <p class='bjava'>
037 *    <jc>// A page builder attaches the declaration; the resource serves it with a store lookup.</jc>
038 *    TableSpec.<jsm>create</jsm>(<js>"orders"</js>).detail(RowDetails.<jsm>order</jsm>());
039 * </p>
040 */
041@SuppressWarnings({
042   "java:S1192" // Column and field names repeat as inline literals by design.
043})
044public final class RowDetails {
045
046   private RowDetails() {}
047
048   private static <T> Function<String,T> none() {
049      return id -> null;
050   }
051
052   /** @return The Pets table detail, for building a page. */
053   public static RowDetail pet() {
054      return pet(none());
055   }
056
057   /**
058    * The Pets table detail: Details, this pet's orders and its history.
059    *
060    * @param lookup Finds a pet by row id, or returns <jk>null</jk>.
061    * @return A new detail.
062    */
063   public static RowDetail pet(Function<String,Pet> lookup) {
064      return RowDetail.fromBean(Pet.class, lookup).basePath("/console/pets").title("{name}").columns(3)
065         .section("Details", "id", "name", "species", "status", "price", "tags")
066         .label("price", "Price (USD)")
067         .tab(DetailTab.table("Orders", ordersView("pets-orders", "/console/pets/orders/rows")).id("orders").scopeParam("petId").countFromRows())
068         .tab(DetailTab.table("History", historyView()).id("history").scopeParam("petId").countFromRows());
069   }
070
071   /** @return The Sold table detail, for building a page. */
072   public static RowDetail soldPet() {
073      return soldPet(none());
074   }
075
076   /**
077    * The Sold table detail: the columns that table shows, and this pet's orders.
078    *
079    * @param lookup Finds a pet by row id, or returns <jk>null</jk>.
080    * @return A new detail.
081    */
082   public static RowDetail soldPet(Function<String,Pet> lookup) {
083      return RowDetail.fromBean(Pet.class, lookup).key("soldPet").basePath("/console/pets").title("{name}").columns(3)
084         .section("Details", "id", "name", "species", "price")
085         .label("price", "Price (USD)")
086         .tab(DetailTab.table("Orders", ordersView("sold-orders", "/console/pets/orders/rows")).id("orders").scopeParam("petId").countFromRows());
087   }
088
089   /** @return The Orders and Store tables' detail, for building a page. */
090   public static RowDetail order() {
091      return order(none());
092   }
093
094   /**
095    * The Orders and Store recent-orders detail: Details and the ordered pet.
096    *
097    * @param lookup Finds an order by row id, or returns <jk>null</jk>.
098    * @return A new detail.
099    */
100   public static RowDetail order(Function<String,Order> lookup) {
101      return RowDetail.fromBean(Order.class, lookup).basePath("/console/orders").title("Order {id}").columns(2)
102         .section("Details", "id", "petId", "shipDate", "status")
103         .label("petId", "Pet")
104         .tab(DetailTab.table("Pet", TableSpec.create("order-pet").rowType("Pet").dataMode(DataMode.CLIENT).dataUrl("/console/orders/pet/rows")
105            .columns(
106               Column.create("id").label("Id"),
107               Column.create("name").label("Name").href("/console/pets/{id}/summary"),
108               Column.create("species").label("Species"),
109               Column.create("status").label("Status"),
110               Column.create("price").label("Price (USD)").render(Render.of("decimal").meta("places", 2))))
111            .id("pet").scopeParam("orderId").countFromRows());
112   }
113
114   /** @return The Users table detail, for building a page. */
115   public static RowDetail user() {
116      return user(none());
117   }
118
119   /**
120    * The Users table detail: Details and the orders this user placed.  It serves {@code UserView}, so a password,
121    * email or phone never leaves the server.
122    *
123    * @param lookup Finds a user by row id (the username), or returns <jk>null</jk>.
124    * @return A new detail.
125    */
126   public static RowDetail user(Function<String,UserView> lookup) {
127      return RowDetail.fromBean(UserView.class, lookup).key("user").basePath("/console/users").title("{username}").columns(2)
128         .section("Details", "username", "firstName", "lastName", "userStatus")
129         .label("userStatus", "Status")
130         .tab(DetailTab.table("Orders", ordersView("user-orders", "/console/users/orders/rows")).id("orders").scopeParam("username").countFromRows());
131   }
132
133   /** @return The Pending changes table detail. */
134   public static RowDetail change() {
135      return RowDetail.inline().title("Change {id}").section("Details", "id", "summary", "author", "state", "message");
136   }
137
138   /** @return The Audit table detail. */
139   public static RowDetail audit() {
140      return RowDetail.inline().title("{action} {entity} {entityId}")
141         .section("Details", "id", "at", "actor", "action", "entity", "entityId", "detail");
142   }
143
144   /** @return The pet History table detail. */
145   public static RowDetail history() {
146      return RowDetail.inline().title("{action}").section("Details", "id", "at", "actor", "action", "detail");
147   }
148
149   private static TableSpec ordersView(String id, String dataUrl) {
150      return TableSpec.create(id).rowType("Order").dataMode(DataMode.CLIENT).dataUrl(dataUrl)
151         .columns(
152            Column.create("id").label("Order").href("/console/pets/{petId}/orders/{id}").searchType(SearchType.NUMERIC),
153            Column.create("shipDate").label("Ship date").render("date").searchType(SearchType.TEXT),
154            Column.create("status").label("Status").render(PillRenders.orderStatus()).searchType(SearchType.ENUM));
155   }
156
157   private static TableSpec historyView() {
158      return TableSpec.create("pets-history").rowType("AuditEntry").dataMode(DataMode.CLIENT).dataUrl("/console/pets/history/rows")
159         .columns(
160            Column.create("at").label("When").render("timestamp").searchType(SearchType.TEXT),
161            Column.create("actor").label("Actor").searchType(SearchType.TEXT),
162            Column.create("action").label("Action").render(PillRenders.auditAction()).searchType(SearchType.ENUM),
163            Column.create("detail").label("Detail").searchType(SearchType.TEXT));
164   }
165}