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 0b12ec1..dd0041c 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 @@ -5,7 +5,13 @@ import play.api.libs.json.{Format, Json} object AdminGetTables { case class Response(data: List[Response.DatabaseTable]) object Response { - case class DatabaseTable(name: String, columns: List[TableColumn], primaryKeyName: String, canBeDeleted: Boolean) + case class DatabaseTable( + name: String, + columns: List[TableColumn], + primaryKeyName: String, + canBeDeleted: Boolean, + referenceDisplayField: Option[String] + ) case class TableColumn( name: String, `type`: String, diff --git a/spra-play-server/src/main/resources/application.conf b/spra-play-server/src/main/resources/application.conf index a6845b0..1fe710f 100644 --- a/spra-play-server/src/main/resources/application.conf +++ b/spra-play-server/src/main/resources/application.conf @@ -28,6 +28,7 @@ dataExplorer { createFilter { requiredColumns = ["user_id", "message"] } + referenceDisplayField = "email" } } } 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 a444431..7cd9dd3 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 @@ -40,13 +40,14 @@ case class TableSettings( primaryKeyDataType: PrimaryKeyDataType = PrimaryKeyDataType.UUID, columnTypeOverrides: Map[String, CustomDataType] = Map.empty, filterableColumns: List[String] = List.empty, - createSettings: CreateSettings = CreateSettings() + createSettings: CreateSettings = CreateSettings(), + referenceDisplayField: Option[String] = None ) { 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)""" + filterableColumns = $filterableColumns, createSettings = $createSettings, referenceDisplayField: $referenceDisplayField)""" } object TableSettings { @@ -102,7 +103,8 @@ object TableSettings { createSettings = CreateSettings( requiredColumns = getList[String]("createFilter.requiredColumns"), nonRequiredColumns = getList[String]("createFilter.nonRequiredColumns") - ) + ), + referenceDisplayField = getOption[String]("referenceDisplayField") ) } } 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 011c032..c7b3f21 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 @@ -4,7 +4,7 @@ import net.wiringbits.spra.admin.config.{CustomDataType, DataExplorerConfig, Tab import net.wiringbits.spra.admin.repositories.DatabaseTablesRepository import net.wiringbits.spra.admin.repositories.models.{ForeignKey, TableData} import net.wiringbits.spra.admin.utils.models.QueryParameters -import net.wiringbits.spra.admin.utils.{MapStringHideExt, contentRangeHeader} +import net.wiringbits.spra.admin.utils.contentRangeHeader import net.wiringbits.spra.api.models.* import java.awt.image.BufferedImage @@ -78,17 +78,14 @@ class AdminService @Inject() ( name = settings.tableName, columns = columns, primaryKeyName = settings.primaryKeyField, - canBeDeleted = settings.canBeDeleted + canBeDeleted = settings.canBeDeleted, + referenceDisplayField = settings.referenceDisplayField ) } } } yield AdminGetTables.Response(items) } - private def hideData(tableData: TableData, hiddenColumns: List[String]) = { - tableData.data.hideData(hiddenColumns) - } - def tableMetadata(tableName: String, queryParams: QueryParameters): Future[(List[Map[String, String]], String)] = { val validations = { for { @@ -102,9 +99,8 @@ class AdminService @Inject() ( _ <- validateQueryParameters(tableName, queryParams) tableRows <- databaseTablesRepository.getTableMetadata(settings, queryParams) numberOfRecords <- databaseTablesRepository.numberOfRecords(tableName) - hiddenTableData = tableRows.map(data => hideData(data, settings.hiddenColumns)) contentRange = contentRangeHeader(tableName, queryParams, numberOfRecords) - } yield (hiddenTableData, contentRange) + } yield (tableRows.map(_.data), contentRange) } private def validateQueryParameters(tableName: String, params: QueryParameters): Future[Unit] = { @@ -129,9 +125,7 @@ class AdminService @Inject() ( _ <- validations maybe <- databaseTablesRepository.find(tableName, primaryKeyValue) tableRow = maybe.getOrElse(throw new RuntimeException(s"Cannot find item in $tableName with id $primaryKeyValue")) - settings = dataExplorerConfig.unsafeFindByName(tableName) - hiddenData = hideData(tableRow, settings.hiddenColumns) - } yield hiddenData + } yield tableRow.data } def find(tableName: String, primaryKeyValues: List[String]): Future[List[Map[String, String]]] = { @@ -141,7 +135,6 @@ class AdminService @Inject() ( for { _ <- validations - settings = dataExplorerConfig.unsafeFindByName(tableName) tableRows <- Future.sequence { primaryKeyValues.map { primaryKeyValue => for { @@ -152,8 +145,7 @@ class AdminService @Inject() ( } yield tableData } } - maskedTableData = tableRows.map(x => hideData(x, settings.hiddenColumns)) - } yield maskedTableData + } yield tableRows.map(_.data) } def create(tableName: String, request: AdminCreateTable.Request): Future[String] = { diff --git a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/package.scala b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/package.scala index 4135e43..87a8c72 100644 --- a/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/package.scala +++ b/spra-play-server/src/main/scala/net/wiringbits/spra/admin/utils/package.scala @@ -17,12 +17,6 @@ package object utils { } } - implicit class MapStringHideExt(val data: Map[String, String]) extends AnyVal { - def hideData(columnsToHide: List[String]): Map[String, String] = { - data.filterNot { case (key, _) => columnsToHide.contains(key) } - } - } - def contentRangeHeader(tableName: String, queryParameters: QueryParameters, numberOfRecords: Int): String = { s"$tableName ${queryParameters.pagination.start}-${queryParameters.pagination.end}/$numberOfRecords" } diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/CreateGuesser.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/CreateGuesser.scala index 36fbc9c..d348793 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/CreateGuesser.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/CreateGuesser.scala @@ -38,7 +38,13 @@ object CreateGuesser { reference = reference, isRequired = isRequired, validate = required - )(SelectInput(optionText = source, isRequired = isRequired, validate = required)) + )( + SelectInput( + optionText = props.response.referenceDisplayField.getOrElse(source), + isRequired = isRequired, + validate = required + ) + ) } } .getOrElse(Fragment()) 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 99fe257..5b468f9 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 @@ -39,7 +39,13 @@ object EditGuesser { ReferenceInput( source = field.name, reference = reference - )(SelectInput(optionText = source, disabled = field.disabled)) + )( + SelectInput( + source = source, + optionText = props.response.referenceDisplayField.getOrElse(source), + disabled = field.disabled + ) + ) } } diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/ListGuesser.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/ListGuesser.scala index 6b6978e..eacb477 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/ListGuesser.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/components/ListGuesser.scala @@ -35,7 +35,9 @@ object ListGuesser { case Image => ImageField(source = field.name, sx = styles) case Number => NumberField(source = field.name) case ColumnType.Reference(reference, source) => - defaultField(reference, field.name)(TextField(source = source)) + defaultField(reference, field.name)( + TextField(source = props.response.referenceDisplayField.getOrElse(source)) + ) } } } diff --git a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/SelectInput.scala b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/SelectInput.scala index c040bf7..16e2c8a 100644 --- a/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/SelectInput.scala +++ b/spra-web/src/main/scala/net/wiringbits/spra/ui/web/facades/reactadmin/SelectInput.scala @@ -7,18 +7,20 @@ import scala.scalajs.js.| object SelectInput extends ExternalComponent { case class Props( - optionText: String = "", + source: String = "", disabled: Boolean = false, isRequired: Boolean = false, - validate: js.UndefOr[js.Any] = js.undefined + validate: js.UndefOr[js.Any] = js.undefined, + optionText: js.UndefOr[String] = js.undefined ) def apply( - optionText: String = "", + source: String = "", disabled: Boolean = false, isRequired: Boolean = false, - validate: js.UndefOr[js.Any] = js.undefined - ): BuildingComponent[_, _] = super.apply(Props(optionText, disabled, isRequired, validate)) + validate: js.UndefOr[js.Any] = js.undefined, + optionText: js.UndefOr[String] = js.undefined + ): BuildingComponent[_, _] = super.apply(Props(source, disabled, isRequired, validate, optionText)) override val component: String | js.Object = ReactAdmin.SelectInput }