Skip to content

Commit

Permalink
Added Companies UI
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-podzigun committed Jun 30, 2018
1 parent ad6aea9 commit b0877cc
Show file tree
Hide file tree
Showing 16 changed files with 379 additions and 18 deletions.
5 changes: 4 additions & 1 deletion project/src/main/scala/definitions/AdminClientApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import org.scalajs.sbtplugin.cross.CrossProject
import sbt.Keys._
import sbt._
import scommons.sbtplugin.project.CommonModule
import scoverage.ScoverageKeys._

object AdminClientApi {

val id: String = "scommons-admin-client-api"

def base: File = file(id)

private lazy val `scommons-admin-client-api`: CrossProject = crossProject.crossType(CrossType.Pure).in(base)
private lazy val `scommons-admin-client-api`: CrossProject = crossProject
.crossType(CrossType.Pure).in(base)
.settings(CommonModule.settings: _*)
.settings(AdminModule.settings: _*)
.settings(
Expand All @@ -25,6 +27,7 @@ object AdminClientApi {
// Add JVM-specific settings here
).jsSettings(
// Add JS-specific settings here
coverageEnabled := false
)

lazy val jvm: Project = `scommons-admin-client-api`.jvm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package scommons.admin.client

import io.github.shogowada.scalajs.reactjs.React.Props
import io.github.shogowada.scalajs.reactjs.ReactDOM
import io.github.shogowada.scalajs.reactjs.VirtualDOM._
import io.github.shogowada.scalajs.reactjs.classes.ReactClass
import io.github.shogowada.scalajs.reactjs.redux.ReactRedux._
import io.github.shogowada.scalajs.reactjs.redux.Redux.Dispatch
import io.github.shogowada.scalajs.reactjs.redux.{ReactRedux, Redux}
import io.github.shogowada.scalajs.reactjs.router.WithRouter
import io.github.shogowada.scalajs.reactjs.router.dom.RouterDOM._
import org.scalajs.dom
import scommons.client.app._
import scommons.client.task._
import scommons.client.ui.Buttons

import scala.scalajs.js.annotation.JSExportTopLevel

Expand All @@ -14,26 +24,57 @@ object AdminMain {

dom.document.title = "scommons-admin"

// val store = Redux.createStore(AdminReducer.reduce)
//
// val appMainPanelProps = AppMainPanelProps(
// name = "scommons-admin",
// user = "me",
// copyright = "© scommons-admin",
// version = "(version: 0.1.0-SNAPSHOT)"
// )
val store = Redux.createStore(AdminStateReducer.reduce)

val appMainPanelProps = AppMainPanelProps(
name = "scommons-admin",
user = "me",
copyright = "© scommons-admin",
version = "(version: 0.1.0-SNAPSHOT)"
)

ReactDOM.render(
// <.Provider(^.store := store)(
// <.HashRouter()(
// <(WithRouter(AppMainPanel()))(^.wrapped := appMainPanelProps)(
// <(RouteController()).empty,
// <(TaskController()).empty
// )
// )
// ),
<.div(^.className := AdminImagesCss.computer)(),
<.Provider(^.store := store)(
<.HashRouter()(
<(WithRouter(AppMainPanel()))(^.wrapped := appMainPanelProps)(
<(RouteController()).empty,
<(TaskController()).empty
)
)
),
mountNode
)
}
}

object RouteController {

def apply(): ReactClass = reactClass

private lazy val reactClass = ReactRedux.connectAdvanced(
(dispatch: Dispatch) => {

(state: AdminState, _: Props[Unit]) => {
AppBrowseControllerProps(
List(Buttons.REFRESH, Buttons.ADD, Buttons.REMOVE, Buttons.EDIT),
AdminStateReducer.getTreeRoots(state),
dispatch
)
}
}
)(AppBrowseController())
}

object TaskController {

def apply(): ReactClass = reactClass

private lazy val reactClass = ReactRedux.connectAdvanced(
(_: Dispatch) => {

(state: AdminState, _: Props[Unit]) => {
TaskManagerProps(state.currentTask)
}
}
)(TaskManager())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package scommons.admin.client

import scommons.admin.client.action.ApiActions
import scommons.admin.client.company._
import scommons.client.task.AbstractTask.AbstractTaskKey
import scommons.client.task.TaskAction
import scommons.client.ui.tree._
import scommons.client.ui.{ButtonImagesCss, Buttons}
import scommons.client.util.{ActionsData, BrowsePath}

case class AdminState(currentTask: Option[AbstractTaskKey],
companyState: CompanyState)

object AdminStateReducer {

val companiesItem = BrowseTreeItemData(
"Companies",
BrowsePath("/companies"),
Some(ButtonImagesCss.folder),
ActionsData(Set(Buttons.REFRESH.command), dispatch => {
case Buttons.REFRESH.command => dispatch(ApiActions.companyListFetch(dispatch))
}),
Some(CompanyPanelController())
)

def getTreeRoots(state: AdminState): List[BrowseTreeData] = List(
companiesItem
)

def reduce(state: Option[AdminState], action: Any): AdminState = AdminState(
currentTask = currentTaskReducer(state.flatMap(_.currentTask), action),
companyState = CompanyStateReducer(state.map(_.companyState), action)
)

private def currentTaskReducer(currentTask: Option[AbstractTaskKey],
action: Any): Option[AbstractTaskKey] = action match {

case a: TaskAction => Some(a.task.key)
case _ => None
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package scommons.admin.client.action

import org.scalajs.dom
import scommons.admin.client.api.AdminUiApiClient
import scommons.admin.client.company.action._
import scommons.api.http.js.JsApiHttpClient

object ApiActions extends CompanyActions {

private val baseUrl = {
val loc = dom.window.location
s"${loc.protocol}//${loc.host}/scommons-admin/ui"
}

protected val client: AdminUiApiClient = {
new AdminUiApiClient(new JsApiHttpClient(baseUrl))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package scommons.admin.client.company

import io.github.shogowada.scalajs.reactjs.React
import io.github.shogowada.scalajs.reactjs.React.Props
import io.github.shogowada.scalajs.reactjs.VirtualDOM._
import io.github.shogowada.scalajs.reactjs.classes.ReactClass
import io.github.shogowada.scalajs.reactjs.redux.ReactRedux
import io.github.shogowada.scalajs.reactjs.redux.Redux.Dispatch
import scommons.admin.client.AdminState
import scommons.admin.client.action.ApiActions
import scommons.admin.client.company.action._
import scommons.client.ui.page._
import scommons.client.ui.popup.{InputPopup, InputPopupProps}
import scommons.client.ui.table._
import scommons.client.ui.{Buttons, ButtonsPanel, ButtonsPanelProps}
import scommons.client.util.ActionsData

object CompanyPanelController {

def apply(): ReactClass = reactClass
private lazy val reactClass = createComp

private def createComp = ReactRedux.connectAdvanced(
(dispatch: Dispatch) => {
(state: AdminState, _: Props[Unit]) => {
CompanyPanelProps(dispatch, state.companyState)
}
}
)(CompanyPanel())
}

case class CompanyPanelProps(dispatch: Dispatch, state: CompanyState)

object CompanyPanel {

def apply(): ReactClass = reactClass
private lazy val reactClass = createComp

private def createComp = React.createClass[CompanyPanelProps, Unit](
componentDidMount = { self =>
val props = self.props.wrapped
if (props.state.dataList.isEmpty) {
props.dispatch(ApiActions.companyListFetch(props.dispatch, props.state.offset))
}
},
render = { self =>
val props = self.props.wrapped

val header = List(
TableColumnData("Id"),
TableColumnData("Company Name")
)

val rows = props.state.dataList.map { data =>
val id = data.id.getOrElse(0).toString
TableRowData(id, List(id, data.name))
}

val selectedData = props.state.dataList.find(_.id == props.state.selectedId)

val limit = CompanyActions.listLimit
val totalPages = PaginationPanel.toTotalPages(props.state.totalCount.getOrElse(0), limit)
val selectedPage = math.min(totalPages, PaginationPanel.toPage(props.state.offset.getOrElse(0), limit))

<.div()(
<(ButtonsPanel())(^.wrapped := ButtonsPanelProps(
List(Buttons.ADD, Buttons.EDIT),
ActionsData(Set(Buttons.ADD.command) ++ selectedData.map(_ => Buttons.EDIT.command), dispatch => {
case Buttons.ADD.command => dispatch(CompanyCreateRequestAction(create = true))
case Buttons.EDIT.command => dispatch(CompanyUpdateRequestAction(update = true))
}),
props.dispatch
))(),

<(TablePanel())(^.wrapped := TablePanelProps(
header,
rows,
props.state.selectedId.map(_.toString).toSet,
onSelect = { row =>
props.dispatch(CompanySelectedAction(row.id.toInt))
}
))(),

<(PaginationPanel())(^.wrapped := PaginationPanelProps(totalPages, selectedPage, onPage = { page =>
props.dispatch(ApiActions.companyListFetch(props.dispatch,
offset = Some(PaginationPanel.toOffset(page, limit))
))
}))(),

<(InputPopup())(^.wrapped := InputPopupProps(
props.state.showCreatePopup,
"Enter Company name:",
onOk = { text =>
props.dispatch(CompanyCreateRequestAction(create = false))
props.dispatch(ApiActions.companyCreate(props.dispatch, text))
},
onCancel = { () =>
props.dispatch(CompanyCreateRequestAction(create = false))
},
initialValue = "New Company"
))(),

selectedData.map { data =>
<(InputPopup())(^.wrapped := InputPopupProps(
props.state.showEditPopup,
"Enter new Company name:",
onOk = { text =>
props.dispatch(CompanyUpdateRequestAction(update = false))
props.dispatch(ApiActions.companyUpdate(props.dispatch, data.copy(name = text)))
},
onCancel = { () =>
props.dispatch(CompanyUpdateRequestAction(update = false))
},
initialValue = data.name
))()
}
)
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package scommons.admin.client.company

import scommons.admin.client.api.company.CompanyData
import scommons.admin.client.company.action._

case class CompanyState(dataList: List[CompanyData] = Nil,
offset: Option[Int] = None,
totalCount: Option[Int] = None,
selectedId: Option[Int] = None,
showCreatePopup: Boolean = false,
showEditPopup: Boolean = false)

object CompanyStateReducer {

def apply(state: Option[CompanyState], action: Any): CompanyState = {
reduce(state.getOrElse(CompanyState()), action)
}

private def reduce(state: CompanyState, action: Any): CompanyState = action match {
case a: CompanyCreateRequestAction => state.copy(showCreatePopup = a.create)
case a: CompanyUpdateRequestAction => state.copy(showEditPopup = a.update)
case a: CompanySelectedAction => state.copy(selectedId = Some(a.id))
case a: CompanyListFetchAction => state.copy(offset = a.offset)
case CompanyListFetchedAction(dataList, totalCount) => state.copy(
dataList = dataList,
totalCount = totalCount.orElse(state.totalCount)
)
case CompanyCreatedAction(data) => state.copy(dataList = state.dataList :+ data)
case CompanyUpdatedAction(data) => state.copy(dataList = state.dataList.map {
case curr if curr.id == data.id => data
case curr => curr
})
case _ => state
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package scommons.admin.client.company.action

import io.github.shogowada.scalajs.reactjs.redux.Redux.Dispatch
import scommons.admin.client.api.company._
import scommons.api.ApiStatus.Ok
import scommons.client.task.FutureTask

import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success

trait CompanyActions {

protected def client: CompanyApi

def companyListFetch(dispatch: Dispatch,
offset: Option[Int] = None,
limit: Option[Int] = Some(CompanyActions.listLimit),
symbols: Option[String] = None): CompanyListFetchAction = {

val future = client.listCompanies(offset, limit, symbols).andThen {
case Success(CompanyListResp(Ok, Some(dataList), totalCount)) =>
dispatch(CompanyListFetchedAction(dataList, totalCount))
}

CompanyListFetchAction(FutureTask("Fetching Companies", future), offset)
}

def companyCreate(dispatch: Dispatch, name: String): CompanyCreateAction = {
val future = client.createCompany(CompanyData(None, name)).andThen {
case Success(CompanyResp(Ok, Some(data))) =>
dispatch(CompanyCreatedAction(data))
}

CompanyCreateAction(FutureTask("Creating Company", future))
}

def companyUpdate(dispatch: Dispatch, data: CompanyData): CompanyUpdateAction = {
val future = client.updateCompany(data).andThen {
case Success(CompanyResp(Ok, Some(respData))) =>
dispatch(CompanyUpdatedAction(respData))
}

CompanyUpdateAction(FutureTask("Updating Company", future))
}
}

object CompanyActions {

val listLimit = 10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scommons.admin.client.company.action

import scommons.admin.client.api.company.CompanyResp
import scommons.client.task.{FutureTask, TaskAction}

case class CompanyCreateAction(task: FutureTask[CompanyResp]) extends TaskAction
Loading

0 comments on commit b0877cc

Please sign in to comment.