diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala index 836acaf..b3b01f6 100644 --- a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala +++ b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala @@ -41,6 +41,9 @@ def nested(prefixes: String*): MessageCatalogue = NestedMessageCatalogue(this, prefixes*) + def withPrefixes(prefixes: String*): MessageCatalogue = + NestedMessageCatalogue(this, prefixes*) + private class NestedMessageCatalogue( underlying: MessageCatalogue, prefixes: String* @@ -67,3 +70,6 @@ } .headOption .orElse(underlying.get(msg)) + + override def withPrefixes(prefixes: String*): MessageCatalogue = + underlying.withPrefixes(prefixes*) diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala index 836acaf..b3b01f6 100644 --- a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala +++ b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala @@ -41,6 +41,9 @@ def nested(prefixes: String*): MessageCatalogue = NestedMessageCatalogue(this, prefixes*) + def withPrefixes(prefixes: String*): MessageCatalogue = + NestedMessageCatalogue(this, prefixes*) + private class NestedMessageCatalogue( underlying: MessageCatalogue, prefixes: String* @@ -67,3 +70,6 @@ } .headOption .orElse(underlying.get(msg)) + + override def withPrefixes(prefixes: String*): MessageCatalogue = + underlying.withPrefixes(prefixes*) diff --git a/core/shared/src/main/scala/works/iterative/core/Moment.scala b/core/shared/src/main/scala/works/iterative/core/Moment.scala index d3bd41f..542d94e 100644 --- a/core/shared/src/main/scala/works/iterative/core/Moment.scala +++ b/core/shared/src/main/scala/works/iterative/core/Moment.scala @@ -23,3 +23,5 @@ given Conversion[Instant, Moment] = Moment(_) given Conversion[Moment, Instant] = _.toInstant + + given (using ord: Ordering[Instant]): Ordering[Moment] = ord diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala index 836acaf..b3b01f6 100644 --- a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala +++ b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala @@ -41,6 +41,9 @@ def nested(prefixes: String*): MessageCatalogue = NestedMessageCatalogue(this, prefixes*) + def withPrefixes(prefixes: String*): MessageCatalogue = + NestedMessageCatalogue(this, prefixes*) + private class NestedMessageCatalogue( underlying: MessageCatalogue, prefixes: String* @@ -67,3 +70,6 @@ } .headOption .orElse(underlying.get(msg)) + + override def withPrefixes(prefixes: String*): MessageCatalogue = + underlying.withPrefixes(prefixes*) diff --git a/core/shared/src/main/scala/works/iterative/core/Moment.scala b/core/shared/src/main/scala/works/iterative/core/Moment.scala index d3bd41f..542d94e 100644 --- a/core/shared/src/main/scala/works/iterative/core/Moment.scala +++ b/core/shared/src/main/scala/works/iterative/core/Moment.scala @@ -23,3 +23,5 @@ given Conversion[Instant, Moment] = Moment(_) given Conversion[Moment, Instant] = _.toInstant + + given (using ord: Ordering[Instant]): Ordering[Moment] = ord diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala new file mode 100644 index 0000000..69c24af --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala @@ -0,0 +1,27 @@ +package works.iterative.tapir +package endpoints + +import sttp.tapir.Codec +import sttp.tapir.CodecFormat.TextPlain +import zio.json.JsonCodec +import sttp.tapir.Schema +import sttp.tapir.Endpoint + +type BaseEndpoint = Endpoint[Unit, Unit, Unit, Unit, Any] + +trait RepositoryEndpointsModule[K: Codec[ + String, + *, + TextPlain +], V: JsonCodec: Schema, F: JsonCodec: Schema]( + name: String, + base: BaseEndpoint +) extends CustomTapir: + val load: Endpoint[Unit, K, Unit, Option[V], Any] = base.get + .in("view" / name / path[K]("id")) + .out(jsonBody[Option[V]]) + + val find: Endpoint[Unit, F, Unit, List[V], Any] = base.post + .in("view" / name) + .in(jsonBody[F]) + .out(jsonBody[List[V]]) diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala index 836acaf..b3b01f6 100644 --- a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala +++ b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala @@ -41,6 +41,9 @@ def nested(prefixes: String*): MessageCatalogue = NestedMessageCatalogue(this, prefixes*) + def withPrefixes(prefixes: String*): MessageCatalogue = + NestedMessageCatalogue(this, prefixes*) + private class NestedMessageCatalogue( underlying: MessageCatalogue, prefixes: String* @@ -67,3 +70,6 @@ } .headOption .orElse(underlying.get(msg)) + + override def withPrefixes(prefixes: String*): MessageCatalogue = + underlying.withPrefixes(prefixes*) diff --git a/core/shared/src/main/scala/works/iterative/core/Moment.scala b/core/shared/src/main/scala/works/iterative/core/Moment.scala index d3bd41f..542d94e 100644 --- a/core/shared/src/main/scala/works/iterative/core/Moment.scala +++ b/core/shared/src/main/scala/works/iterative/core/Moment.scala @@ -23,3 +23,5 @@ given Conversion[Instant, Moment] = Moment(_) given Conversion[Moment, Instant] = _.toInstant + + given (using ord: Ordering[Instant]): Ordering[Moment] = ord diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala new file mode 100644 index 0000000..69c24af --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala @@ -0,0 +1,27 @@ +package works.iterative.tapir +package endpoints + +import sttp.tapir.Codec +import sttp.tapir.CodecFormat.TextPlain +import zio.json.JsonCodec +import sttp.tapir.Schema +import sttp.tapir.Endpoint + +type BaseEndpoint = Endpoint[Unit, Unit, Unit, Unit, Any] + +trait RepositoryEndpointsModule[K: Codec[ + String, + *, + TextPlain +], V: JsonCodec: Schema, F: JsonCodec: Schema]( + name: String, + base: BaseEndpoint +) extends CustomTapir: + val load: Endpoint[Unit, K, Unit, Option[V], Any] = base.get + .in("view" / name / path[K]("id")) + .out(jsonBody[Option[V]]) + + val find: Endpoint[Unit, F, Unit, List[V], Any] = base.post + .in("view" / name) + .in(jsonBody[F]) + .out(jsonBody[List[V]]) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 2e7479c..1c2a6f8 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -24,10 +24,16 @@ def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) + def withPrefixes(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) + object ComponentContext: - case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) - extends ComponentContext[App]: + case class Nested[Env](parent: ComponentContext[Env], prefixes: Seq[String]) + extends ComponentContext[Env]: export parent.{messages => _, *} override lazy val messages: MessageCatalogue = parent.messages.nested(prefixes*) + + override def withPrefixes(prefixes: String*): ComponentContext[Env] = + parent.withPrefixes(prefixes*) diff --git a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala index b144740..dd0e8c1 100644 --- a/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala +++ b/codecs/src/main/scala/works/iterative/core/codecs/Codecs.scala @@ -42,6 +42,10 @@ given JsonCodec[PlainOneLine] = textCodec(PlainOneLine.apply) given JsonCodec[Markdown] = textCodec(Markdown.apply) + given JsonCodec[PermissionOp] = + JsonCodec.string.transform(PermissionOp(_), _.value) + given JsonCodec[PermissionTarget] = textCodec(PermissionTarget.apply) + given JsonCodec[UserId] = JsonCodec.string.transform(auth.UserId.unsafe(_), _.value) @@ -65,6 +69,8 @@ ValidatedStringFactory[A] ): Schema[A] = Schema.string + given Schema[PermissionOp] = Schema.string + given Schema[PermissionTarget] = Schema.string given Schema[PlainMultiLine] = Schema.string given Schema[PlainOneLine] = Schema.string given Schema[Markdown] = Schema.string diff --git a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala index 836acaf..b3b01f6 100644 --- a/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala +++ b/core/shared/src/main/scala/works/iterative/core/MessageCatalogue.scala @@ -41,6 +41,9 @@ def nested(prefixes: String*): MessageCatalogue = NestedMessageCatalogue(this, prefixes*) + def withPrefixes(prefixes: String*): MessageCatalogue = + NestedMessageCatalogue(this, prefixes*) + private class NestedMessageCatalogue( underlying: MessageCatalogue, prefixes: String* @@ -67,3 +70,6 @@ } .headOption .orElse(underlying.get(msg)) + + override def withPrefixes(prefixes: String*): MessageCatalogue = + underlying.withPrefixes(prefixes*) diff --git a/core/shared/src/main/scala/works/iterative/core/Moment.scala b/core/shared/src/main/scala/works/iterative/core/Moment.scala index d3bd41f..542d94e 100644 --- a/core/shared/src/main/scala/works/iterative/core/Moment.scala +++ b/core/shared/src/main/scala/works/iterative/core/Moment.scala @@ -23,3 +23,5 @@ given Conversion[Instant, Moment] = Moment(_) given Conversion[Moment, Instant] = _.toInstant + + given (using ord: Ordering[Instant]): Ordering[Moment] = ord diff --git a/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala new file mode 100644 index 0000000..69c24af --- /dev/null +++ b/tapir/shared/src/main/scala/works/iterative/tapir/endpoints/RepositoryEndpointsModule.scala @@ -0,0 +1,27 @@ +package works.iterative.tapir +package endpoints + +import sttp.tapir.Codec +import sttp.tapir.CodecFormat.TextPlain +import zio.json.JsonCodec +import sttp.tapir.Schema +import sttp.tapir.Endpoint + +type BaseEndpoint = Endpoint[Unit, Unit, Unit, Unit, Any] + +trait RepositoryEndpointsModule[K: Codec[ + String, + *, + TextPlain +], V: JsonCodec: Schema, F: JsonCodec: Schema]( + name: String, + base: BaseEndpoint +) extends CustomTapir: + val load: Endpoint[Unit, K, Unit, Option[V], Any] = base.get + .in("view" / name / path[K]("id")) + .out(jsonBody[Option[V]]) + + val find: Endpoint[Unit, F, Unit, List[V], Any] = base.post + .in("view" / name) + .in(jsonBody[F]) + .out(jsonBody[List[V]]) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala index 2e7479c..1c2a6f8 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/ComponentContext.scala @@ -24,10 +24,16 @@ def nested(prefixes: String*): ComponentContext[Env] = ComponentContext.Nested[Env](this, prefixes) + def withPrefixes(prefixes: String*): ComponentContext[Env] = + ComponentContext.Nested[Env](this, prefixes) + object ComponentContext: - case class Nested[App](parent: ComponentContext[App], prefixes: Seq[String]) - extends ComponentContext[App]: + case class Nested[Env](parent: ComponentContext[Env], prefixes: Seq[String]) + extends ComponentContext[Env]: export parent.{messages => _, *} override lazy val messages: MessageCatalogue = parent.messages.nested(prefixes*) + + override def withPrefixes(prefixes: String*): ComponentContext[Env] = + parent.withPrefixes(prefixes*) diff --git a/ui/shared/src/main/scala/works/iterative/ui/services/ClientEntityService.scala b/ui/shared/src/main/scala/works/iterative/ui/services/ClientEntityService.scala new file mode 100644 index 0000000..82186c3 --- /dev/null +++ b/ui/shared/src/main/scala/works/iterative/ui/services/ClientEntityService.scala @@ -0,0 +1,8 @@ +package works.iterative.ui.services + +import zio.* +import works.iterative.core.* + +trait ClientEntityService[Id, Entity, Command]: + def load(id: Id): IO[UserMessage, Entity] + def update(id: Id, command: Command): IO[UserMessage, Option[UserMessage]]