/** * @license * Copyright (c) 2014, 2024, Oracle and/or its affiliates. * Licensed under The Universal Permissive License (UPL), Version 1.0 * as shown at https://oss.oracle.com/licenses/upl/ * @ignore */ import * as AccUtils from "../accUtils"; import * as ko from "knockout"; import "ojs/ojselectsingle"; import "ojs/ojlabel"; import "ojs/ojchart"; import "ojs/ojlistview"; import "ojs/ojavatar"; import { ObservableKeySet } from "ojs/ojknockout-keyset"; import { ojListView } from "ojs/ojlistview"; import { RESTDataProvider } from "ojs/ojrestdataprovider"; import { TextFilter } from "ojs/ojdataprovider"; import { ojDialog } from "ojs/ojdialog"; import "ojs/ojdialog"; import "ojs/ojinputtext"; import { ojButtonEventMap } from "ojs/ojbutton"; type Activity = { id: number; }; type Item = { id: number; name: string; short_desc: string; price: number; quantity: number; quantity_shipped: number; quantity_instock: number; activity_id: number; image: string; value?: string; }; type ActivityItems = { id: number; name: string; items: Array; short_desc: string; image: string; }; class DashboardViewModel { // Activity key attribute that you'll pass as a parameter when creating // RESTDataProvider instance keyAttributes = "id"; // REST endpoint that returns Activity data restServerURLActivities = "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/"; // RESTDataProvider instance activityDataProvider: RESTDataProvider; // Initialize activityKey to 3 to construct an initial REST call activityKey: number = 3; // Initialize placeholder RESTDataProvider for activity items itemsDataProvider: RESTDataProvider = new RESTDataProvider({ keyAttributes: "id", url: "", transforms: { fetchFirst: { request: null!, response: (): any => { return { data: [] }; }, }, }, }); // REST endpoint that returns activity item data restServerURLItems = "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/" + this.activityKey + "/items/"; // itemsArray!: Array; itemData: ko.Observable; pieSeriesValue: ko.ObservableArray; // Observables for Activities selectedActivity = new ObservableKeySet(); activitySelected = ko.observable(false); // Controls display of Activity Items firstSelectedActivity = ko.observable(); selectedActivityIds = ko.observable(); // Observables for Activity Items itemSelected = ko.observable(false); selectedItem = ko.observable(); firstSelectedItem = ko.observable(); dataFetched = ko.observable(true); fetchError: string = (''); // Fields in Create dialog itemName: ko.Observable; price: ko.Observable; short_desc: ko.Observable; quantity_instock: ko.Observable; quantity_shipped: ko.Observable; quantity: number; inputImageFile: string = "css/images/product_images/jet_logo_256.png"; constructor() { this.activityDataProvider = new RESTDataProvider({ keyAttributes: this.keyAttributes, url: this.restServerURLActivities, error: this.fetchErrorHandler, transforms: { fetchFirst: { request: async (options) => { const url = new URL(options.url); const { size, offset } = options.fetchParameters; url.searchParams.set("limit", String(size)); url.searchParams.set("offset", String(offset)); return new Request(url.href); }, response: async ({ body }) => { const { items, totalSize, hasMore } = body; return { data: items, totalSize, hasMore }; }, }, }, }); this.itemData = ko.observable(''); this.pieSeriesValue = ko.observableArray([{}]); let pieSeries = [ { name: "Quantity in Stock", items: [this.itemData().quantity_instock] }, { name: "Quantity Shipped", items: [this.itemData().quantity_shipped] }, ]; this.pieSeriesValue(pieSeries); // Initialize fields in create dialog this.itemName = ko.observable(null); this.price = ko.observable(null); this.short_desc = ko.observable(null); this.quantity_instock = ko.observable(null); this.quantity_shipped = ko.observable(null); this.quantity = 0; // inputImageFile has already been initialized. } // end constructor // Open dialog public showCreateDialog(event: ojButtonEventMap["ojAction"]) { (document.getElementById("createDialog") as ojDialog).open(); } // Create item and close dialog public createItem = async (event: ojButtonEventMap["ojAction"]) => { (document.getElementById("createDialog") as ojDialog).close(); } /** * Handle failure to fetch Activities list data */ fetchErrorHandler = (errorDetail: RESTDataProvider.FetchErrorDetail | RESTDataProvider.FetchResponseErrorDetail) => { this.dataFetched(false); if (errorDetail.hasOwnProperty('response')) { this.fetchError = `${(errorDetail as RESTDataProvider.FetchResponseErrorDetail).response.status}`; } else { this.fetchError = (errorDetail as RESTDataProvider.FetchErrorDetail).error.message; } } /** * Handle selection from Activities list */ selectedActivityChanged = (event: ojListView.firstSelectedItemChanged ) => { /** * If no items are selected then the firstSelectedItem property returns an object * with both key and data properties set to null. */ let itemContext = event.detail.value.data; if (itemContext != null) { // If selection, populate and display list // Hide currently-selected activity item this.activitySelected(false); this.activityKey = event.detail.value.data.id; this.restServerURLItems = "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/" + this.activityKey + "/items/"; // Create the itemsDataProvider instance of RESTDataProvider this.itemsDataProvider = new RESTDataProvider({ keyAttributes: this.keyAttributes, capabilities: { filter: { textFilter: true, }, }, url: this.restServerURLItems, textFilterAttributes: ["name"], transforms: { fetchFirst: { request: async (options) => { const url = new URL(options.url); const { size, offset } = options.fetchParameters; url.searchParams.set("limit", String(size)); url.searchParams.set("offset", String(offset)); const filterCriterion = options.fetchParameters .filterCriterion as TextFilter; const { textFilterAttributes } = options.fetchOptions; if ( filterCriterion && filterCriterion.text && textFilterAttributes ) { const { text } = filterCriterion; textFilterAttributes.forEach((attribute) => { url.searchParams.set(attribute, text); }); } return new Request(url.href); }, response: async ({ body }) => { const { items, totalSize, hasMore } = body; return { data: items, totalSize, hasMore }; }, }, }, }); // Set List View properties this.activitySelected(true); this.itemSelected(false); this.selectedItem(); this.itemData(); } else { // If deselection, hide list this.activitySelected(false); this.itemSelected(false); } }; /** * Handle selection from Activity Items list */ selectedItemChanged = (event: ojListView.firstSelectedItemChanged) => { let isClicked = event.detail.value.data; if (isClicked != null) { // If selection, populate and display list this.itemData(event.detail.value.data); // Create variable and get attributes of the items list to set pie chart values let pieSeries = [ { name: "Quantity in Stock", items: [this.itemData().quantity_instock] }, { name: "Quantity Shipped", items: [this.itemData().quantity_shipped] } ]; // Update the pie chart with the data this.pieSeriesValue(pieSeries); this.itemSelected(true); } else { // If deselection, hide list this.itemSelected(false); } }; /** * Optional ViewModel method invoked after the View is inserted into the * document DOM. The application can put logic that requires the DOM being * attached here. * This method might be called multiple times - after the View is created * and inserted into the DOM and after the View is reconnected * after being disconnected. */ connected(): void { AccUtils.announce("Dashboard page loaded."); document.title = "Dashboard"; // implement further logic if needed } /** * Optional ViewModel method invoked after the View is disconnected from the DOM. */ disconnected(): void { // implement if needed } /** * Optional ViewModel method invoked after transition to the new View is complete. * That includes any possible animation between the old and the new View. */ transitionCompleted(): void { // implement if needed } } export = DashboardViewModel;