Skip to content

Commit

Permalink
New Dashboard widgets for 2.10 ER1 (#2194)
Browse files Browse the repository at this point in the history
* Skip cuke redirect to upgrade for multiple_services

* adds products and backend_apis index

* integrates rails with new dashboard widgets

* add widget to first page

* adds missing field for last updated date

* * adds boilerplate for backends and products index pages* adds missing fields

* lots more updates

* adds missing paths to prop "links"

* removes TODO comments

* updates to api page

* adds missing field system_name

* 🚧 more efficient

* add backend table

* adds missing system_name

* more updates to backend

* ✅ test for services index

* Update test/integration/api/services_controller_test.rb

Co-authored-by: Marta <Martouta@users.noreply.github.com>

* adds missing fields

* adds missing system_name

* rebase

* ✏️ fixes typo

* extracts data-list componet

* fix merge conflict

* work on pagination

* fix pagination

* adds itemCount prop for backends and products

* jose suggested changes

* reorganizes imports

* removes console logs

* typo

* reduces verbosity

* fixes indentation

* fixes close with click outside effect

* ✅ fixes test due to new PF version

* 🚨 makes Flow happy

* 🚧 search

* Sphinx search Service

* Sphinx search BackendAPI

* fixes products widget's icon

* fixes widgets' links

* adds missing link for Edit page

* fixes console error intruduced by isPlainButtonAction

* ✅ 🥒

* ✅ fixes controller test

* 💩 we need to fix this test

* Fixing provider_side.feature

* Cuke Fix '/p/signup/...' not found for missing the host

* fixes

* Add JS to follow 'API'

Co-authored-by: Marta Noya <mnoyabon@redhat.com>
Co-authored-by: Christie Molloy <cmolloy@redhat.com>
Co-authored-by: Marta <Martouta@users.noreply.github.com>
Co-authored-by: Damian Peralta <damianperaltam@gmail.com>
  • Loading branch information
5 people authored Oct 14, 2020
1 parent 76be205 commit 5e45439
Show file tree
Hide file tree
Showing 54 changed files with 1,020 additions and 172 deletions.
13 changes: 13 additions & 0 deletions app/controllers/api/services_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class Api::ServicesController < Api::BaseController
include ServiceDiscovery::ControllerMethods
include SearchSupport
include ThreeScale::Search::Helpers

activate_menu :serviceadmin, :overview

Expand All @@ -14,6 +16,17 @@ class Api::ServicesController < Api::BaseController
actions.sublayout 'api/service'
end

def index
activate_menu :dashboard
search = ThreeScale::Search.new(params[:search] || params)
@raw_services = current_user.accessible_services
@services = @raw_services.order(updated_at: :desc)
.paginate(pagination_params)
.scope_search(search)
.decorate
.to_json(only: %i[name updated_at id system_name], methods: %i[links apps_count backends_count unread_alerts_count])
end

def show
@service = @service.decorate
end
Expand Down
14 changes: 14 additions & 0 deletions app/controllers/provider/admin/backend_apis_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
# frozen_string_literal: true

class Provider::Admin::BackendApisController < Provider::Admin::BaseController
include SearchSupport
include ThreeScale::Search::Helpers

load_and_authorize_resource :backend_api, through: :current_user, through_association: :accessible_backend_apis

activate_menu :backend_api, :overview
layout 'provider'

def index
activate_menu :dashboard
search = ThreeScale::Search.new(params[:search] || params)
@raw_backend_apis = current_account.backend_apis
@backend_apis = @raw_backend_apis.order(updated_at: :desc)
.paginate(pagination_params)
.scope_search(search)
.decorate
.to_json(only: %i[name updated_at id private_endpoint system_name], methods: %i[links products_count])
end

def new
activate_menu :dashboard
end
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/provider/admin/dashboards_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ def show
@services = current_user.accessible_services
@messages_presenter = current_presenter
@unread_messages_presenter = unread_messages_presenter

@widget_products = @services.order(updated_at: :desc)
.decorate
.take(5)
.to_json(only: %i[name updated_at id], methods: %i[link links])
@widget_backends = current_account.backend_apis.includes(:services)
.order(updated_at: :desc)
.decorate
.take(5)
.to_json(only: %i[name updated_at id], methods: %i[link links])
end

include DashboardTimeRange
Expand Down
4 changes: 4 additions & 0 deletions app/decorators/application_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ def api_selector_api_link
def self.collection_decorator_class
PaginatingDecorator
end

def updated_at
object.updated_at.to_s :long if object.updated_at?
end
end
16 changes: 16 additions & 0 deletions app/decorators/backend_api_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@ def api_selector_api_link
h.provider_admin_backend_api_path(object)
end

alias link api_selector_api_link

private

def backend_api?
true
end

def links
[
{ name: 'Edit', path: h.edit_provider_admin_backend_api_path(object) },
{ name: 'Overview', path: h.provider_admin_backend_api_path(object) },
{ name: 'Analytics', path: h.provider_admin_backend_api_stats_usage_path(object) },
{ name: 'Methods & Metrics', path: h.provider_admin_backend_api_metrics_path(object) },
{ name: 'Mapping Rules', path: h.provider_admin_backend_api_mapping_rules_path(object) },
]
end

def products_count
object.services.size
end
end
29 changes: 28 additions & 1 deletion app/decorators/service_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

class ServiceDecorator < ApplicationDecorator

self.include_root_in_json = false

def link_to_application_plans
Expand Down Expand Up @@ -46,9 +45,37 @@ def api_selector_api_link
end
end

alias link api_selector_api_link

private

def backend_api?
false
end

def links
[
{ name: 'Edit', path: h.edit_admin_service_path(object) },
{ name: 'Overview', path: h.admin_service_path(object) },
{ name: 'Analytics', path: h.admin_service_stats_usage_path(object) },
{ name: 'Applications', path: h.admin_service_applications_path(object) },
{ name: 'ActiveDocs', path: h.admin_service_api_docs_path(object) },
{ name: 'Integration', path: h.path_to_service(object) },
]
end

def apps_count
cinstances.size
end

def backends_count
backend_api_configs.size
end

def unread_alerts_count
account.buyer_alerts
.by_service(object)
.unread
.size
end
end
5 changes: 5 additions & 0 deletions app/indices/backend_api_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

ThinkingSphinx::Index.define(:backend_api, with: :real_time) do
indexes :name, as: :name
end
5 changes: 5 additions & 0 deletions app/indices/service_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

ThinkingSphinx::Index.define(:service, with: :real_time) do
indexes :name, as: :name
end
1 change: 1 addition & 0 deletions app/javascript/packs/PF4Styles/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'patternflyStyles/dashboard'
15 changes: 15 additions & 0 deletions app/javascript/packs/backend_apis_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BackendsIndexPageWrapper } from 'BackendApis/components/IndexPage'
import { safeFromJsonString } from 'utilities/json-utils'

const containerId = 'backend-apis'

document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById(containerId)

const { backends, backendsCount } = container.dataset

BackendsIndexPageWrapper({
backends: safeFromJsonString(backends),
backendsCount: safeFromJsonString(backendsCount)
}, containerId)
})
16 changes: 16 additions & 0 deletions app/javascript/packs/dashboard_backends_widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BackendsWidgetWrapper } from 'Dashboard/components/BackendsWidget'
import { safeFromJsonString } from 'utilities/json-utils'

const containerId = 'backends-widget'

document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById(containerId)

const { newBackendPath, backendsPath, backends } = safeFromJsonString(container.dataset.backendsWidget)

BackendsWidgetWrapper({
newBackendPath,
backendsPath,
backends: safeFromJsonString(backends)
}, containerId)
})
16 changes: 16 additions & 0 deletions app/javascript/packs/dashboard_products_widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ProductsWidgetWrapper } from 'Dashboard/components/ProductsWidget'
import { safeFromJsonString } from 'utilities/json-utils'

const containerId = 'products-widget'

document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById(containerId)

const { newProductPath, productsPath, products } = safeFromJsonString(container.dataset.productsWidget)

ProductsWidgetWrapper({
newProductPath,
productsPath,
products: safeFromJsonString(products)
}, containerId)
})
15 changes: 15 additions & 0 deletions app/javascript/packs/products_index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ProductsIndexPageWrapper } from 'Products/components/IndexPage'
import { safeFromJsonString } from 'utilities/json-utils'

const containerId = 'products'

document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById(containerId)

const { products, productsCount } = container.dataset

ProductsIndexPageWrapper({
products: safeFromJsonString(products),
productsCount: safeFromJsonString(productsCount)
}, containerId)
})
162 changes: 162 additions & 0 deletions app/javascript/src/BackendApis/components/IndexPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// @flow

import React from 'react'
import {
Form,
Level,
LevelItem,
InputGroup,
TextInput,
Button,
ButtonVariant,
PageSection,
Pagination as PFPagination,
PaginationVariant,
PageSectionVariants,
Title,
Divider,
Toolbar,
ToolbarItem
} from '@patternfly/react-core'
import {
Table,
TableHeader,
TableBody
} from '@patternfly/react-table'
import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'
import { createReactWrapper } from 'utilities/createReactWrapper'

import 'BackendApis/styles/backends.scss'

type Props = {
backendsCount: number,
backends: Array<{
id: number,
system_name: string,
private_endpoint: string,
link: string,
links: Array<{
name: string,
path: string
}>,
name: string,
products_count: number,
type: string,
updated_at: string,
}>
}

const BackendsIndexPage = ({ backendsCount, backends }: Props) => {
const tableColumns = [
'Name',
'System name',
'Last updated',
'Private base URL',
'Linked products'
]

const tableRows = backends.map((tableRow) => ({
cells: [
{ title: <Button href={tableRow.links[1].path} component="a" variant="link" isInline>{tableRow.name}</Button> },
tableRow.system_name,
<span className="api-table-timestamp">{tableRow.updated_at}</span>,
tableRow.private_endpoint,
tableRow.products_count
]
}))

const linkToPage = (rowId, actionNumber) => {
const { path } = backends[rowId].links[actionNumber]
window.location.href = path
}

const tableActions = ['Edit', 'Overview', 'Analytics', 'Methods and Metrics', 'Mapping Rules'].map((title, i) => ({
title,
onClick: (_event, rowId) => linkToPage(rowId, i)
}))

const url = new URL(window.location.href)

const selectPerPage = (_event, selectedPerPage) => {
url.searchParams.set('per_page', selectedPerPage)
url.searchParams.delete('page')
window.location.href = url.toString()
}

const goToPage = (page) => {
url.searchParams.set('page', page)
window.location.href = url.toString()
}

const Pagination = ({ variant }: { variant?: string }) => {
const perPage = url.searchParams.get('per_page')
const page = url.searchParams.get('page')
return (
<PFPagination
widgetId="pagination-options-menu-top"
itemCount={backendsCount}
perPage={Number(perPage) || 20}
page={Number(page)}
onPerPageSelect={selectPerPage}
onNextClick={(_ev, page) => goToPage(page)}
onPreviousClick={(_ev, page) => goToPage(page)}
onFirstClick={(_ev, page) => goToPage(page)}
onLastClick={(_ev, page) => goToPage(page)}
perPageOptions={[ { title: '10', value: 10 }, { title: '20', value: 20 } ]}
variant={variant}
/>
)
}

return (
<PageSection className="api-table-page-section" variant={PageSectionVariants.light}>
<Level>
<LevelItem>
<Title headingLevel="h1" size="2xl">Backends</Title>
</LevelItem>
<LevelItem>
<Button variant="primary" component="a" href="/p/admin/backend_apis/new">
Create Backend
</Button>
</LevelItem>
</Level>
<p className="api-table-description">
Explore and manage all your internal APIs.
</p>
<Divider/>
<Toolbar id="top-toolbar" className="pf-c-toolbar">
<div className="pf-c-toolbar__content">
<ToolbarItem>
<Form id="new_search" action="/p/admin/backend_apis" acceptCharset="UTF-8" method="get">
<InputGroup className="api-table-search">
<input name="utf8" type="hidden" value="✓" />
<TextInput placeholder="Find a Backend" name="search[query]" id="findBackend" type="search" aria-label="Find a backend" />
<Button variant={ButtonVariant.control} aria-label="search button for search input" type="submit">
<SearchIcon />
</Button>
</InputGroup>
</Form>
</ToolbarItem>
<ToolbarItem className="api-toolbar-pagination" align={{ default: 'alignRight' }}>
<Pagination />
</ToolbarItem>
</div>
</Toolbar>
<Table aria-label="Actions Table" actions={tableActions} cells={tableColumns} rows={tableRows}>
<TableHeader />
<TableBody />
</Table>
<Toolbar id="bottom-toolbar" className="pf-c-toolbar">
<div className="pf-c-toolbar__content">
<ToolbarItem className="api-toolbar-pagination" align={{ default: 'alignRight' }}>
<Pagination variant={PaginationVariant.bottom} />
</ToolbarItem>
</div>
</Toolbar>
</PageSection>
)
}

const BackendsIndexPageWrapper = (props: Props, containerId: string) => createReactWrapper(<BackendsIndexPage {...props} />, containerId)

export { BackendsIndexPageWrapper }
Loading

0 comments on commit 5e45439

Please sign in to comment.