From 024c784727bd1691f974e0b949bb18473f4f3e7a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 28 Sep 2023 22:46:03 -0700 Subject: [PATCH 1/3] Display reference tables --- .../spra/api/models/AdminGetTables.scala | 5 +- .../src/main/resources/application.conf | 5 ++ .../spra/admin/config/TableSettings.scala | 26 ++++++++- .../DatabaseTablesRepository.scala | 15 ++++- .../repositories/daos/DatabaseTablesDAO.scala | 55 ++++--------------- .../spra/admin/services/AdminService.scala | 3 +- .../spra/admin/utils/QueryBuilder.scala | 31 +++++++++++ .../spra/admin/utils/StringRegex.scala | 7 --- .../spra/admin/StringRegexSpec.scala | 43 --------------- .../spra/ui/web/components/EditGuesser.scala | 15 ++++- .../reactadmin/ReferenceManyField.scala | 14 +++++ .../ui/web/facades/reactadmin/package.scala | 2 +- 12 files changed, 117 insertions(+), 104 deletions(-) delete mode 100644 spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/StringRegex.scala delete mode 100644 spra-play-server/src/test/scala/net/wiringbits/spra/admin/StringRegexSpec.scala create mode 100644 spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/ReferenceManyField.scala diff --git a/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala b/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala index dd0041c..dc0fcf0 100644 --- a/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala +++ b/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala @@ -10,7 +10,8 @@ object AdminGetTables { columns: List[TableColumn], primaryKeyName: String, canBeDeleted: Boolean, - referenceDisplayField: Option[String] + referenceDisplayField: Option[String], + manyToOneReferences: List[ManyToOneReference] ) case class TableColumn( name: String, @@ -22,9 +23,11 @@ object AdminGetTables { isRequiredOnCreate: Option[Boolean] ) case class TableReference(referencedTable: String, referenceField: String) + case class ManyToOneReference(tableName: String, source: String) implicit val adminTableReferenceResponseFormat: Format[TableReference] = Json.format[TableReference] implicit val adminTableColumnResponseFormat: Format[TableColumn] = Json.format[TableColumn] + implicit val adminManyToOneReferenceResponseFormat: Format[ManyToOneReference] = Json.format[ManyToOneReference] implicit val adminDatabaseTableResponseFormat: Format[DatabaseTable] = Json.format[DatabaseTable] } implicit val adminGetTablesResponseFormat: Format[Response] = Json.format[Response] diff --git a/spra-play-server/src/main/resources/application.conf b/spra-play-server/src/main/resources/application.conf index 1fe710f..0b1f575 100644 --- a/spra-play-server/src/main/resources/application.conf +++ b/spra-play-server/src/main/resources/application.conf @@ -17,6 +17,11 @@ dataExplorer { requiredColumns = ["name", "email", "password"] nonRequiredColumns = ["last_name"] } + manyToOneReferences { + user_logs { + source = "message" + } + } } userLogs { diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala index 7cd9dd3..9d3fe02 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala @@ -1,6 +1,7 @@ package net.wiringbits.spra.admin.config import com.typesafe.config.Config +import net.wiringbits.spra.api.models.AdminGetTables.Response.ManyToOneReference import play.api.ConfigLoader import scala.util.Try @@ -41,13 +42,15 @@ case class TableSettings( columnTypeOverrides: Map[String, CustomDataType] = Map.empty, filterableColumns: List[String] = List.empty, createSettings: CreateSettings = CreateSettings(), - referenceDisplayField: Option[String] = None + referenceDisplayField: Option[String] = None, + manyToOneReferences: List[ManyToOneReference] = List.empty ) { override def toString: String = s"""TableSettings(tableName = $tableName, primaryKeyField = $primaryKeyField, referenceField = $referenceField, hiddenColumns = $hiddenColumns, nonEditableColumns = $nonEditableColumns, canBeDeleted = $canBeDeleted, primaryKeyDataType = $primaryKeyDataType, columnTypeOverrides = $columnTypeOverrides, - filterableColumns = $filterableColumns, createSettings = $createSettings, referenceDisplayField: $referenceDisplayField)""" + filterableColumns = $filterableColumns, createSettings = $createSettings, referenceDisplayField: $referenceDisplayField, + manyToOneReferences: $manyToOneReferences)""" } object TableSettings { @@ -85,6 +88,22 @@ object TableSettings { ).getOrElse(Map.empty) } + def handleManyToOneReferences: List[ManyToOneReference] = Try( + newConfig + .getConfig("manyToOneReferences") + .resolve() + .entrySet() + .asScala + .map { entry => + entry.getKey.split('.').toList match + case tableName :: _ => + val source = newConfig.getConfig(s"manyToOneReferences.$tableName").getString("source") + ManyToOneReference(tableName, source) + case _ => throw new RuntimeException(s"Invalid manyToOneReferences") + } + .toList + ).getOrElse(List.empty) + TableSettings( tableName = get[String]("tableName"), primaryKeyField = get[String]("primaryKeyField"), @@ -104,7 +123,8 @@ object TableSettings { requiredColumns = getList[String]("createFilter.requiredColumns"), nonRequiredColumns = getList[String]("createFilter.nonRequiredColumns") ), - referenceDisplayField = getOption[String]("referenceDisplayField") + referenceDisplayField = getOption[String]("referenceDisplayField"), + manyToOneReferences = handleManyToOneReferences ) } } diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/DatabaseTablesRepository.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/DatabaseTablesRepository.scala index 4319c31..4db448d 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/DatabaseTablesRepository.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/DatabaseTablesRepository.scala @@ -4,7 +4,7 @@ import net.wiringbits.spra.admin.config.{DataExplorerConfig, TableSettings} import net.wiringbits.spra.admin.executors.DatabaseExecutionContext import net.wiringbits.spra.admin.repositories.daos.DatabaseTablesDAO import net.wiringbits.spra.admin.repositories.models.{DatabaseTable, ForeignKey, TableColumn, TableData} -import net.wiringbits.spra.admin.utils.models.QueryParameters +import net.wiringbits.spra.admin.utils.models.{FilterParameter, QueryParameters} import play.api.db.Database import javax.inject.Inject @@ -42,7 +42,18 @@ class DatabaseTablesRepository @Inject() (database: Database)(implicit def getTableMetadata(settings: TableSettings, queryParameters: QueryParameters): Future[List[TableData]] = Future { database.withTransaction { implicit conn => val columns = DatabaseTablesDAO.getTableColumns(settings.tableName) - val rows = DatabaseTablesDAO.getTableData(settings, columns, queryParameters, dataExplorerConfig.baseUrl) + val fieldsAndValues = queryParameters.filters.map { case FilterParameter(field, value) => + val tableColumn = + columns.find(_.name == field).getOrElse(throw new RuntimeException(s"Invalid property in filters: $field")) + (tableColumn, value) + }.toMap + val rows = DatabaseTablesDAO.getTableData( + settings = settings, + columns = columns, + queryParameters = queryParameters, + baseUrl = dataExplorerConfig.baseUrl, + fieldsAndValues = fieldsAndValues + ) val columnNames = getColumnNames(columns, settings.primaryKeyField) rows.map { row => val tableRow = row.convertToMap(columnNames) diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/daos/DatabaseTablesDAO.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/daos/DatabaseTablesDAO.scala index 2aed493..b8a06ce 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/daos/DatabaseTablesDAO.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/repositories/daos/DatabaseTablesDAO.scala @@ -3,11 +3,10 @@ package net.wiringbits.spra.admin.repositories.daos import anorm.{SqlParser, SqlStringInterpolation} import net.wiringbits.spra.admin.config.{CustomDataType, PrimaryKeyDataType, TableSettings} import net.wiringbits.spra.admin.repositories.models.* +import net.wiringbits.spra.admin.utils.QueryBuilder import net.wiringbits.spra.admin.utils.models.{FilterParameter, QueryParameters} -import net.wiringbits.spra.admin.utils.{QueryBuilder, StringRegex} -import java.sql.{Connection, Date, PreparedStatement, ResultSet} -import java.time.LocalDate +import java.sql.{Connection, PreparedStatement, ResultSet} import java.util.UUID import scala.collection.mutable.ListBuffer import scala.util.Try @@ -77,37 +76,17 @@ object DatabaseTablesDAO { settings: TableSettings, columns: List[TableColumn], queryParameters: QueryParameters, - baseUrl: String + baseUrl: String, + fieldsAndValues: Map[TableColumn, String] )(implicit conn: Connection): List[TableRow] = { - val dateRegex = StringRegex.dateRegex - val limit = queryParameters.pagination.end - queryParameters.pagination.start - val offset = queryParameters.pagination.start val tableName = settings.tableName - // react-admin gives us a "id" field instead of the primary key of the actual column so we need to replace it - val sortBy = if (queryParameters.sort.field == "id") settings.primaryKeyField else queryParameters.sort.field - - val conditionsSql = queryParameters.filters - .map { case FilterParameter(filterField, filterValue) => - filterValue match { - case dateRegex(_, _, _) => - s"DATE($filterField) = ?" - - case _ => - if (filterValue.toIntOption.isDefined || filterValue.toDoubleOption.isDefined) - s"$filterField = ?" - else - s"$filterField LIKE ?" - } - } - .mkString("WHERE ", " AND ", " ") - val sql = - s""" - SELECT * FROM $tableName - ${if (queryParameters.filters.nonEmpty) conditionsSql else ""} - ORDER BY $sortBy ${queryParameters.sort.ordering} - LIMIT $limit OFFSET $offset - """ + val sql = QueryBuilder.get( + tableName, + fieldsAndValues = fieldsAndValues, + queryParameters = queryParameters, + primaryKeyField = settings.primaryKeyField + ) val preparedStatement = conn.prepareStatement(sql) queryParameters.filters.zipWithIndex @@ -115,19 +94,7 @@ object DatabaseTablesDAO { // We have to increment index by 1 because SQL parameterIndex starts in 1 val sqlIndex = index + 1 - filterValue match { - case dateRegex(year, month, day) => - val parsedDate = LocalDate.of(year.toInt, month.toInt, day.toInt) - preparedStatement.setDate(sqlIndex, Date.valueOf(parsedDate)) - - case _ => - if (filterValue.toIntOption.isDefined) - preparedStatement.setInt(sqlIndex, filterValue.toInt) - else if (filterValue.toDoubleOption.isDefined) - preparedStatement.setDouble(sqlIndex, filterValue.toDouble) - else - preparedStatement.setString(sqlIndex, s"%$filterValue%") - } + preparedStatement.setObject(sqlIndex, filterValue) } val resultSet = preparedStatement.executeQuery() diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/services/AdminService.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/services/AdminService.scala index c7b3f21..b5e3915 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/services/AdminService.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/services/AdminService.scala @@ -79,7 +79,8 @@ class AdminService @Inject() ( columns = columns, primaryKeyName = settings.primaryKeyField, canBeDeleted = settings.canBeDeleted, - referenceDisplayField = settings.referenceDisplayField + referenceDisplayField = settings.referenceDisplayField, + manyToOneReferences = settings.manyToOneReferences ) } } diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala index 8193eba..1b447ff 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala @@ -2,10 +2,41 @@ package net.wiringbits.spra.admin.utils import net.wiringbits.spra.admin.config.PrimaryKeyDataType import net.wiringbits.spra.admin.repositories.models.TableColumn +import net.wiringbits.spra.admin.utils.models.QueryParameters import scala.collection.mutable object QueryBuilder { + def get( + tableName: String, + fieldsAndValues: Map[TableColumn, String], + queryParameters: QueryParameters, + primaryKeyField: String + ): String = { + val sqlValues = new mutable.StringBuilder() + + val filters = for { + (tableColumn, _) <- fieldsAndValues + _ = sqlValues.append(s"${tableColumn.name} = ?::${tableColumn.`type`} AND ") + } yield + // It is ideal to convert timestamptz to date when comparing dates to avoid the time + if tableColumn.`type` == "timestamptz" then s"${tableColumn.name}::date = ?::date" + else s"${tableColumn.name} = ?::${tableColumn.`type`}" + + // react-admin gives us a "id" field instead of the primary key of the actual column so we need to replace it + val sortBy = if (queryParameters.sort.field == "id") primaryKeyField else queryParameters.sort.field + val limit = queryParameters.pagination.end - queryParameters.pagination.start + val offset = queryParameters.pagination.start + + s""" + |SELECT * + |FROM $tableName + |${if filters.nonEmpty then filters.mkString("WHERE ", " AND ", " ") else ""} + |ORDER BY $sortBy ${queryParameters.sort.ordering} + |LIMIT $limit OFFSET $offset + |""".stripMargin + } + def create( tableName: String, fieldsAndValues: Map[TableColumn, String], diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/StringRegex.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/StringRegex.scala deleted file mode 100644 index ecd3354..0000000 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/StringRegex.scala +++ /dev/null @@ -1,7 +0,0 @@ -package net.wiringbits.spra.admin.utils - -import scala.util.matching.Regex - -object StringRegex { - val dateRegex: Regex = "([12]\\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])".r -} diff --git a/spra-play-server/src/test/scala/net/wiringbits/spra/admin/StringRegexSpec.scala b/spra-play-server/src/test/scala/net/wiringbits/spra/admin/StringRegexSpec.scala deleted file mode 100644 index 8078c67..0000000 --- a/spra-play-server/src/test/scala/net/wiringbits/spra/admin/StringRegexSpec.scala +++ /dev/null @@ -1,43 +0,0 @@ -package net.wiringbits.spra.admin - -import net.wiringbits.spra.admin.utils.StringRegex -import org.scalatest.matchers.must.Matchers.{be, must} -import org.scalatest.wordspec.AnyWordSpec - -class StringRegexSpec extends AnyWordSpec { - "dateRegex" should { - val dateRegex = StringRegex.dateRegex - - val valid = List( - "2023-01-20", - "2012-03-01", - "2020-09-19", - "2024-12-31", - "2002-02-23" - ) - - val invalid = List( - "aaaa-bb-cc", - "2022-a3-23", - "20e1-03-23", - "2004-01-c4", - "2012-01-23-a", - "20230223", - "??????????", - "asdfghjkl", - "ABCDEFGHI" - ) - - valid.foreach { value => - s"accept valid value: $value" in { - dateRegex.matches(value) must be(true) - } - } - - invalid.foreach { value => - s"reject invalid value: $value" in { - dateRegex.matches(value) must be(false) - } - } - } -} diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala index 5b468f9..4457832 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala @@ -1,6 +1,7 @@ package net.wiringbits.spra.ui.web.components import net.wiringbits.spra.api.models.AdminGetTables +import net.wiringbits.spra.api.models.AdminGetTables.Response.ManyToOneReference import net.wiringbits.spra.ui.web.facades.reactadmin.* import net.wiringbits.spra.ui.web.facades.reactadmin.ReactAdmin.useEditContext import net.wiringbits.spra.ui.web.models.{ButtonAction, ColumnType, DataExplorerSettings} @@ -8,8 +9,9 @@ import net.wiringbits.spra.ui.web.utils.ResponseGuesser import org.scalajs.dom import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits.global import slinky.core.facade.{Fragment, ReactElement} -import slinky.core.{FunctionalComponent, KeyAddingStage} +import slinky.core.{BuildingComponent, FunctionalComponent, KeyAddingStage} +import scala.collection.immutable import scala.scalajs.js import scala.util.{Failure, Success} @@ -49,6 +51,15 @@ object EditGuesser { } } + val manyToOneReferences: Seq[ReactElement] = props.response.manyToOneReferences.map { + case ManyToOneReference(tableName, source) => + ReferenceManyField(target = props.response.primaryKeyName, reference = tableName)( + Datagrid(rowClick = "edit", bulkActionButtons = false)( + TextField(source = source) + ) + ).withKey(tableName) + } + def onClick(action: ButtonAction, ctx: js.Dictionary[js.Any]): Unit = { val primaryKey = dom.window.location.hash.split("/").lastOption.getOrElse("") action.onClick(primaryKey).onComplete { @@ -84,6 +95,6 @@ object EditGuesser { Edit(actions)( SimpleForm(toolbar)(inputs) - ) + )(manyToOneReferences: _*) } } diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/ReferenceManyField.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/ReferenceManyField.scala new file mode 100644 index 0000000..868f008 --- /dev/null +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/ReferenceManyField.scala @@ -0,0 +1,14 @@ +package net.wiringbits.spra.ui.web.facades.reactadmin + +import slinky.core.{BuildingComponent, ExternalComponent} + +import scala.scalajs.js + +object ReferenceManyField extends ExternalComponent { + case class Props(target: String, reference: String) + + def apply(target: String, reference: String): BuildingComponent[_, _] = + this.apply(Props(target, reference)) + + override val component: String | js.Object = ReactAdmin.ReferenceManyField +} diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/package.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/package.scala index 6d34e3f..02ea339 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/package.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/package.scala @@ -14,7 +14,7 @@ package object reactadmin { val Admin, Resource, EditGuesser, ListGuesser, TextInput, ImageField, NumberInput, DateTimeInput, ReferenceInput, SelectInput, Button, DeleteButton, SaveButton, TopToolbar, Toolbar, Edit, SimpleForm, DateField, TextField, EmailField, NumberField, ReferenceField, DateInput, FilterButton, ExportButton, List, Datagrid, Create, - CreateButton: js.Object = + CreateButton, ReferenceManyField: js.Object = js.native } } From c8da4b8cb65fc306acce9d193fbf128cf7733cc6 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 28 Sep 2023 22:49:28 -0700 Subject: [PATCH 2/3] Add label --- .../net/wiringbits/spra/api/models/AdminGetTables.scala | 2 +- spra-play-server/src/main/resources/application.conf | 1 + .../net/wiringbits/spra/admin/config/TableSettings.scala | 3 ++- .../net/wiringbits/spra/ui/web/components/EditGuesser.scala | 4 ++-- .../spra/ui/web/facades/reactadmin/TextField.scala | 5 +++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala b/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala index dc0fcf0..cf56229 100644 --- a/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala +++ b/spra-api/shared/src/main/scala/net/wiringbits/spra/api/models/AdminGetTables.scala @@ -23,7 +23,7 @@ object AdminGetTables { isRequiredOnCreate: Option[Boolean] ) case class TableReference(referencedTable: String, referenceField: String) - case class ManyToOneReference(tableName: String, source: String) + case class ManyToOneReference(tableName: String, source: String, label: String) implicit val adminTableReferenceResponseFormat: Format[TableReference] = Json.format[TableReference] implicit val adminTableColumnResponseFormat: Format[TableColumn] = Json.format[TableColumn] diff --git a/spra-play-server/src/main/resources/application.conf b/spra-play-server/src/main/resources/application.conf index 0b1f575..2fdf567 100644 --- a/spra-play-server/src/main/resources/application.conf +++ b/spra-play-server/src/main/resources/application.conf @@ -19,6 +19,7 @@ dataExplorer { } manyToOneReferences { user_logs { + label = "Logs" source = "message" } } diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala index 9d3fe02..d07d9e3 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/config/TableSettings.scala @@ -98,7 +98,8 @@ object TableSettings { entry.getKey.split('.').toList match case tableName :: _ => val source = newConfig.getConfig(s"manyToOneReferences.$tableName").getString("source") - ManyToOneReference(tableName, source) + val label = newConfig.getConfig(s"manyToOneReferences.$tableName").getString("label") + ManyToOneReference(tableName, source, label) case _ => throw new RuntimeException(s"Invalid manyToOneReferences") } .toList diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala index 4457832..607b955 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/EditGuesser.scala @@ -52,10 +52,10 @@ object EditGuesser { } val manyToOneReferences: Seq[ReactElement] = props.response.manyToOneReferences.map { - case ManyToOneReference(tableName, source) => + case ManyToOneReference(tableName, source, label) => ReferenceManyField(target = props.response.primaryKeyName, reference = tableName)( Datagrid(rowClick = "edit", bulkActionButtons = false)( - TextField(source = source) + TextField(source = source, label = label) ) ).withKey(tableName) } diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/TextField.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/TextField.scala index d43f532..af55445 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/TextField.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/TextField.scala @@ -6,9 +6,10 @@ import scala.scalajs.js import scala.scalajs.js.| object TextField extends ExternalComponent { - case class Props(source: String) + case class Props(source: String, label: js.UndefOr[String] = js.undefined) - def apply(source: String): BuildingComponent[_, _] = super.apply(Props(source)) + def apply(source: String, label: js.UndefOr[String] = js.undefined): BuildingComponent[_, _] = + super.apply(Props(source, label)) override val component: String | js.Object = ReactAdmin.TextField } From 89b8d5031ce10962e9b333f7b43c961dd2925d62 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 28 Sep 2023 22:52:14 -0700 Subject: [PATCH 3/3] Remove unused variable --- .../scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala index 1b447ff..a5f2a9a 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/QueryBuilder.scala @@ -13,11 +13,8 @@ object QueryBuilder { queryParameters: QueryParameters, primaryKeyField: String ): String = { - val sqlValues = new mutable.StringBuilder() - val filters = for { (tableColumn, _) <- fieldsAndValues - _ = sqlValues.append(s"${tableColumn.name} = ?::${tableColumn.`type`} AND ") } yield // It is ideal to convert timestamptz to date when comparing dates to avoid the time if tableColumn.`type` == "timestamptz" then s"${tableColumn.name}::date = ?::date"