mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-11-03 18:00:11 +00:00 
			
		
		
		
	First version of new ui based on tailwind
This drops fomantic-ui as css toolkit and introduces tailwindcss. With tailwind there are no predefined components, but it's very easy to create those. So customizing the look&feel is much simpler, most of the time no additional css is needed. This requires a complete rewrite of the markup + styles. Luckily all logic can be kept as is. The now old ui is not removed, it is still available by using a request header `Docspell-Ui` with a value of `1` for the old ui and `2` for the new ui. Another addition is "dev mode", where docspell serves assets with a no-cache header, to disable browser caching. This makes developing a lot easier.
This commit is contained in:
		@@ -40,6 +40,7 @@ case class Duration(nanos: Long) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object Duration {
 | 
			
		||||
  val zero = Duration(0L)
 | 
			
		||||
 | 
			
		||||
  def apply(d: SDur): Duration =
 | 
			
		||||
    Duration(d.toNanos)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								modules/common/src/main/scala/docspell/common/EnvMode.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								modules/common/src/main/scala/docspell/common/EnvMode.scala
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
package docspell.common
 | 
			
		||||
 | 
			
		||||
import cats.implicits._
 | 
			
		||||
 | 
			
		||||
sealed trait EnvMode { self: Product =>
 | 
			
		||||
 | 
			
		||||
  val name: String =
 | 
			
		||||
    productPrefix.toLowerCase
 | 
			
		||||
 | 
			
		||||
  def isDev: Boolean
 | 
			
		||||
  def isProd: Boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
object EnvMode {
 | 
			
		||||
  private val sysProp = "docspell.env"
 | 
			
		||||
  private val envName = "DOCSPELL_ENV"
 | 
			
		||||
 | 
			
		||||
  case object Dev extends EnvMode {
 | 
			
		||||
    val isDev  = true
 | 
			
		||||
    val isProd = false
 | 
			
		||||
  }
 | 
			
		||||
  case object Prod extends EnvMode {
 | 
			
		||||
    val isDev  = false
 | 
			
		||||
    val isProd = true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def dev: EnvMode  = Dev
 | 
			
		||||
  def prod: EnvMode = Prod
 | 
			
		||||
 | 
			
		||||
  def fromString(s: String): Either[String, EnvMode] =
 | 
			
		||||
    s.toLowerCase match {
 | 
			
		||||
      case s if s.startsWith("dev")  => Right(Dev)
 | 
			
		||||
      case s if s.startsWith("prod") => Right(Prod)
 | 
			
		||||
      case _                         => Left(s"Invalid env mode: $s")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  def read: Either[String, Option[EnvMode]] = {
 | 
			
		||||
    def normalize(str: String): Option[String] =
 | 
			
		||||
      Option(str).map(_.trim).filter(_.nonEmpty)
 | 
			
		||||
 | 
			
		||||
    normalize(System.getProperty(sysProp))
 | 
			
		||||
      .orElse(normalize(System.getenv(envName)))
 | 
			
		||||
      .traverse(fromString)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lazy val current: EnvMode =
 | 
			
		||||
    read.toOption.flatten.getOrElse(prod)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ import java.nio.file.{Files, Paths}
 | 
			
		||||
import cats.effect._
 | 
			
		||||
import cats.implicits._
 | 
			
		||||
 | 
			
		||||
import docspell.common.{Banner, Pools, ThreadFactories}
 | 
			
		||||
import docspell.common._
 | 
			
		||||
 | 
			
		||||
import org.log4s._
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +50,10 @@ object Main extends IOApp {
 | 
			
		||||
      Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
 | 
			
		||||
    )
 | 
			
		||||
    logger.info(s"\n${banner.render("***>")}")
 | 
			
		||||
    if (EnvMode.current.isDev) {
 | 
			
		||||
      logger.warn(">>>>>   Docspell is running in DEV mode!   <<<<<")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val pools = for {
 | 
			
		||||
      cec <- connectEC
 | 
			
		||||
      bec <- blockingEC
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import java.nio.file.{Files, Paths}
 | 
			
		||||
import cats.effect._
 | 
			
		||||
import cats.implicits._
 | 
			
		||||
 | 
			
		||||
import docspell.common.{Banner, Pools, ThreadFactories}
 | 
			
		||||
import docspell.common._
 | 
			
		||||
 | 
			
		||||
import org.log4s._
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +57,10 @@ object Main extends IOApp {
 | 
			
		||||
    } yield Pools(cec, bec, blocker, rec)
 | 
			
		||||
 | 
			
		||||
    logger.info(s"\n${banner.render("***>")}")
 | 
			
		||||
    if (EnvMode.current.isDev) {
 | 
			
		||||
      logger.warn(">>>>>   Docspell is running in DEV mode!   <<<<<")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pools.use(p =>
 | 
			
		||||
      RestServer
 | 
			
		||||
        .stream[IO](cfg, p)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,8 @@ import cats.implicits._
 | 
			
		||||
import fs2.Stream
 | 
			
		||||
 | 
			
		||||
import docspell.backend.auth.AuthToken
 | 
			
		||||
import docspell.common.Pools
 | 
			
		||||
import docspell.common._
 | 
			
		||||
import docspell.restserver.http4s.EnvMiddleware
 | 
			
		||||
import docspell.restserver.routes._
 | 
			
		||||
import docspell.restserver.webapp._
 | 
			
		||||
 | 
			
		||||
@@ -39,9 +40,9 @@ object RestServer {
 | 
			
		||||
          adminRoutes(cfg, restApp)
 | 
			
		||||
        },
 | 
			
		||||
        "/api/doc"    -> templates.doc,
 | 
			
		||||
        "/app/assets" -> WebjarRoutes.appRoutes[F](pools.blocker),
 | 
			
		||||
        "/app"        -> templates.app,
 | 
			
		||||
        "/sw.js"      -> templates.serviceWorker,
 | 
			
		||||
        "/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F](pools.blocker)),
 | 
			
		||||
        "/app"        -> EnvMiddleware(templates.app),
 | 
			
		||||
        "/sw.js"      -> EnvMiddleware(templates.serviceWorker),
 | 
			
		||||
        "/"           -> redirectTo("/app")
 | 
			
		||||
      ).orNotFound
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,13 @@
 | 
			
		||||
package docspell.restserver.http4s
 | 
			
		||||
 | 
			
		||||
import cats.Functor
 | 
			
		||||
 | 
			
		||||
import docspell.common._
 | 
			
		||||
 | 
			
		||||
import org.http4s._
 | 
			
		||||
 | 
			
		||||
object EnvMiddleware {
 | 
			
		||||
 | 
			
		||||
  def apply[F[_]: Functor](in: HttpRoutes[F]): HttpRoutes[F] =
 | 
			
		||||
    NoCacheMiddleware.route(EnvMode.current.isDev)(in)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
package docspell.restserver.http4s
 | 
			
		||||
 | 
			
		||||
import cats.Functor
 | 
			
		||||
import cats.data.Kleisli
 | 
			
		||||
import cats.data.NonEmptyList
 | 
			
		||||
 | 
			
		||||
import docspell.common._
 | 
			
		||||
 | 
			
		||||
import org.http4s._
 | 
			
		||||
import org.http4s.headers._
 | 
			
		||||
 | 
			
		||||
object NoCacheMiddleware {
 | 
			
		||||
  private val noCacheHeader: Header =
 | 
			
		||||
    `Cache-Control`(
 | 
			
		||||
      NonEmptyList.of(
 | 
			
		||||
        CacheDirective.`max-age`(Duration.zero.toScala),
 | 
			
		||||
        CacheDirective.`no-store`
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  def apply[F[_]: Functor, G[_], A](
 | 
			
		||||
      noCache: Boolean
 | 
			
		||||
  )(in: Kleisli[F, A, Response[F]]): Kleisli[F, A, Response[F]] =
 | 
			
		||||
    if (noCache) in.map(_.putHeaders(noCacheHeader))
 | 
			
		||||
    else in
 | 
			
		||||
 | 
			
		||||
  def route[F[_]: Functor](noCache: Boolean)(in: HttpRoutes[F]): HttpRoutes[F] =
 | 
			
		||||
    if (noCache) in.map(_.putHeaders(noCacheHeader))
 | 
			
		||||
    else in
 | 
			
		||||
}
 | 
			
		||||
@@ -18,11 +18,12 @@ case class Flags(
 | 
			
		||||
    fullTextSearchEnabled: Boolean,
 | 
			
		||||
    maxPageSize: Int,
 | 
			
		||||
    maxNoteLength: Int,
 | 
			
		||||
    showClassificationSettings: Boolean
 | 
			
		||||
    showClassificationSettings: Boolean,
 | 
			
		||||
    uiVersion: Int
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
object Flags {
 | 
			
		||||
  def apply(cfg: Config): Flags =
 | 
			
		||||
  def apply(cfg: Config, uiVersion: Int): Flags =
 | 
			
		||||
    Flags(
 | 
			
		||||
      cfg.appName,
 | 
			
		||||
      getBaseUrl(cfg),
 | 
			
		||||
@@ -32,7 +33,8 @@ object Flags {
 | 
			
		||||
      cfg.fullTextSearch.enabled,
 | 
			
		||||
      cfg.maxItemPageSize,
 | 
			
		||||
      cfg.maxNoteLength,
 | 
			
		||||
      cfg.showClassificationSettings
 | 
			
		||||
      cfg.showClassificationSettings,
 | 
			
		||||
      uiVersion
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  private def getBaseUrl(cfg: Config): String =
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@ import org.http4s.HttpRoutes
 | 
			
		||||
import org.http4s._
 | 
			
		||||
import org.http4s.dsl.Http4sDsl
 | 
			
		||||
import org.http4s.headers._
 | 
			
		||||
import org.http4s.util.CaseInsensitiveString
 | 
			
		||||
import org.http4s.util.Writer
 | 
			
		||||
import org.log4s._
 | 
			
		||||
import yamusca.implicits._
 | 
			
		||||
import yamusca.imports._
 | 
			
		||||
@@ -24,6 +26,27 @@ object TemplateRoutes {
 | 
			
		||||
  val `text/html`              = new MediaType("text", "html")
 | 
			
		||||
  val `application/javascript` = new MediaType("application", "javascript")
 | 
			
		||||
 | 
			
		||||
  val ui2Header = CaseInsensitiveString("Docspell-Ui2")
 | 
			
		||||
 | 
			
		||||
  case class UiVersion(version: Int) extends Header.Parsed {
 | 
			
		||||
    val key = UiVersion
 | 
			
		||||
    def renderValue(writer: Writer): writer.type =
 | 
			
		||||
      writer.append(version)
 | 
			
		||||
  }
 | 
			
		||||
  object UiVersion extends HeaderKey.Singleton {
 | 
			
		||||
    type HeaderT = UiVersion
 | 
			
		||||
    val name = CaseInsensitiveString("Docspell-Ui")
 | 
			
		||||
    override def parse(s: String): ParseResult[UiVersion] =
 | 
			
		||||
      Either
 | 
			
		||||
        .catchNonFatal(s.trim.toInt)
 | 
			
		||||
        .leftMap(ex => ParseFailure("Invalid int header", ex.getMessage))
 | 
			
		||||
        .map(UiVersion.apply)
 | 
			
		||||
 | 
			
		||||
    override def matchHeader(h: Header): Option[UiVersion] =
 | 
			
		||||
      if (h.name == name) parse(h.value).toOption
 | 
			
		||||
      else None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  trait InnerRoutes[F[_]] {
 | 
			
		||||
    def doc: HttpRoutes[F]
 | 
			
		||||
    def app: HttpRoutes[F]
 | 
			
		||||
@@ -53,22 +76,24 @@ object TemplateRoutes {
 | 
			
		||||
          } yield resp
 | 
			
		||||
        }
 | 
			
		||||
      def app =
 | 
			
		||||
        HttpRoutes.of[F] { case GET -> _ =>
 | 
			
		||||
        HttpRoutes.of[F] { case req @ GET -> _ =>
 | 
			
		||||
          for {
 | 
			
		||||
            templ <- indexTemplate
 | 
			
		||||
            uiv = req.headers.get(UiVersion).map(_.version).getOrElse(1)
 | 
			
		||||
            resp <- Ok(
 | 
			
		||||
              IndexData(cfg).render(templ),
 | 
			
		||||
              IndexData(cfg, uiv).render(templ),
 | 
			
		||||
              `Content-Type`(`text/html`, Charset.`UTF-8`)
 | 
			
		||||
            )
 | 
			
		||||
          } yield resp
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      def serviceWorker =
 | 
			
		||||
        HttpRoutes.of[F] { case GET -> _ =>
 | 
			
		||||
        HttpRoutes.of[F] { case req @ GET -> _ =>
 | 
			
		||||
          for {
 | 
			
		||||
            templ <- swTemplate
 | 
			
		||||
            uiv = req.headers.get(UiVersion).map(_.version).getOrElse(1)
 | 
			
		||||
            resp <- Ok(
 | 
			
		||||
              IndexData(cfg).render(templ),
 | 
			
		||||
              IndexData(cfg, uiv).render(templ),
 | 
			
		||||
              `Content-Type`(`application/javascript`, Charset.`UTF-8`)
 | 
			
		||||
            )
 | 
			
		||||
          } yield resp
 | 
			
		||||
@@ -134,22 +159,28 @@ object TemplateRoutes {
 | 
			
		||||
 | 
			
		||||
  object IndexData {
 | 
			
		||||
 | 
			
		||||
    def apply(cfg: Config): IndexData =
 | 
			
		||||
    def apply(cfg: Config, uiVersion: Int): IndexData =
 | 
			
		||||
      IndexData(
 | 
			
		||||
        Flags(cfg),
 | 
			
		||||
        Seq(
 | 
			
		||||
          "/app/assets" + Webjars.fomanticslimdefault + "/semantic.min.css",
 | 
			
		||||
          s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.css"
 | 
			
		||||
        ),
 | 
			
		||||
        Flags(cfg, uiVersion),
 | 
			
		||||
        chooseUi(uiVersion),
 | 
			
		||||
        Seq(
 | 
			
		||||
          "/app/assets" + Webjars.clipboardjs + "/clipboard.min.js",
 | 
			
		||||
          s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell-app.js"
 | 
			
		||||
        ),
 | 
			
		||||
        s"/app/assets/docspell-webapp/${BuildInfo.version}/favicon",
 | 
			
		||||
        s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.js",
 | 
			
		||||
        Flags(cfg).asJson.spaces2
 | 
			
		||||
        Flags(cfg, uiVersion).asJson.spaces2
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    private def chooseUi(uiVersion: Int): Seq[String] =
 | 
			
		||||
      if (uiVersion == 2)
 | 
			
		||||
        Seq(s"/app/assets/docspell-webapp/${BuildInfo.version}/css/styles.css")
 | 
			
		||||
      else
 | 
			
		||||
        Seq(
 | 
			
		||||
          "/app/assets" + Webjars.fomanticslimdefault + "/semantic.min.css",
 | 
			
		||||
          s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.css"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    implicit def yamuscaValueConverter: ValueConverter[IndexData] =
 | 
			
		||||
      ValueConverter.deriveConverter[IndexData]
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,9 @@ import cats.data.Kleisli
 | 
			
		||||
import cats.data.OptionT
 | 
			
		||||
import cats.effect._
 | 
			
		||||
 | 
			
		||||
import org.http4s.HttpRoutes
 | 
			
		||||
import org.http4s.Method
 | 
			
		||||
import org.http4s.Response
 | 
			
		||||
import org.http4s.StaticFile
 | 
			
		||||
import docspell.common._
 | 
			
		||||
 | 
			
		||||
import org.http4s._
 | 
			
		||||
 | 
			
		||||
object WebjarRoutes {
 | 
			
		||||
 | 
			
		||||
@@ -37,12 +36,13 @@ object WebjarRoutes {
 | 
			
		||||
        if (p.contains("..") || !suffixes.exists(p.endsWith(_)))
 | 
			
		||||
          OptionT.pure(Response.notFound[F])
 | 
			
		||||
        else
 | 
			
		||||
          StaticFile.fromResource(
 | 
			
		||||
            s"/META-INF/resources/webjars$p",
 | 
			
		||||
            blocker,
 | 
			
		||||
            Some(req),
 | 
			
		||||
            true
 | 
			
		||||
          )
 | 
			
		||||
          StaticFile
 | 
			
		||||
            .fromResource(
 | 
			
		||||
              s"/META-INF/resources/webjars$p",
 | 
			
		||||
              blocker,
 | 
			
		||||
              Some(req),
 | 
			
		||||
              EnvMode.current.isProd
 | 
			
		||||
            )
 | 
			
		||||
      case _ =>
 | 
			
		||||
        OptionT.none
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,8 @@
 | 
			
		||||
 | 
			
		||||
    </head>
 | 
			
		||||
 | 
			
		||||
    <body>
 | 
			
		||||
        <div id="docspell-app">
 | 
			
		||||
        </div>
 | 
			
		||||
    <body id="docspell-app">
 | 
			
		||||
        <!-- everything in here gets replaced by elm, including the body tag -->
 | 
			
		||||
 | 
			
		||||
        <script type="application/javascript">
 | 
			
		||||
         var storedAccount = localStorage.getItem('account');
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,7 @@ module Api exposing
 | 
			
		||||
    , getJobQueueState
 | 
			
		||||
    , getJobQueueStateIn
 | 
			
		||||
    , getMailSettings
 | 
			
		||||
    , getNewUi
 | 
			
		||||
    , getNotifyDueItems
 | 
			
		||||
    , getOrgFull
 | 
			
		||||
    , getOrgLight
 | 
			
		||||
@@ -120,6 +121,7 @@ module Api exposing
 | 
			
		||||
    , startOnceScanMailbox
 | 
			
		||||
    , startReIndex
 | 
			
		||||
    , submitNotifyDueItems
 | 
			
		||||
    , toggleNewUi
 | 
			
		||||
    , toggleTags
 | 
			
		||||
    , unconfirmMultiple
 | 
			
		||||
    , updateNotifyDueItems
 | 
			
		||||
@@ -1931,6 +1933,25 @@ getItemProposals flags item receive =
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
toggleNewUi : Flags -> (Result Http.Error BasicResult -> msg) -> Cmd msg
 | 
			
		||||
toggleNewUi flags receive =
 | 
			
		||||
    Http2.authPost
 | 
			
		||||
        { url = flags.config.baseUrl ++ "/api/v1/sec/newui"
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , body = Http.emptyBody
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
getNewUi : Flags -> (Result Http.Error BasicResult -> msg) -> Cmd msg
 | 
			
		||||
getNewUi flags receive =
 | 
			
		||||
    Http2.authGet
 | 
			
		||||
        { url = flags.config.baseUrl ++ "/api/v1/sec/newui"
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helper
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ module App.Data exposing
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.AuthResult exposing (AuthResult)
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.VersionInfo exposing (VersionInfo)
 | 
			
		||||
import Browser exposing (UrlRequest)
 | 
			
		||||
import Browser.Navigation exposing (Key)
 | 
			
		||||
@@ -45,6 +46,7 @@ type alias Model =
 | 
			
		||||
    , userMenuOpen : Bool
 | 
			
		||||
    , subs : Sub Msg
 | 
			
		||||
    , uiSettings : UiSettings
 | 
			
		||||
    , sidebarVisible : Bool
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -92,6 +94,7 @@ init key url flags_ settings =
 | 
			
		||||
      , userMenuOpen = False
 | 
			
		||||
      , subs = Sub.none
 | 
			
		||||
      , uiSettings = settings
 | 
			
		||||
      , sidebarVisible = settings.sideMenuVisible
 | 
			
		||||
      }
 | 
			
		||||
    , Cmd.batch
 | 
			
		||||
        [ Cmd.map UserSettingsMsg uc
 | 
			
		||||
@@ -145,6 +148,8 @@ type Msg
 | 
			
		||||
    | ToggleNavMenu
 | 
			
		||||
    | ToggleUserMenu
 | 
			
		||||
    | GetUiSettings UiSettings
 | 
			
		||||
    | ToggleSidebar
 | 
			
		||||
    | ToggleDarkMode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
defaultPage : Flags -> Page
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import App.Data exposing (..)
 | 
			
		||||
import Browser exposing (UrlRequest(..))
 | 
			
		||||
import Browser.Navigation as Nav
 | 
			
		||||
import Data.Flags
 | 
			
		||||
import Data.UiTheme
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Page.CollectiveSettings.Data
 | 
			
		||||
import Page.CollectiveSettings.Update
 | 
			
		||||
@@ -47,6 +48,38 @@ update msg model =
 | 
			
		||||
updateWithSub : Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
 | 
			
		||||
updateWithSub msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
        ToggleSidebar ->
 | 
			
		||||
            ( { model | sidebarVisible = not model.sidebarVisible }, Cmd.none, Sub.none )
 | 
			
		||||
 | 
			
		||||
        ToggleDarkMode ->
 | 
			
		||||
            let
 | 
			
		||||
                settings =
 | 
			
		||||
                    model.uiSettings
 | 
			
		||||
 | 
			
		||||
                next =
 | 
			
		||||
                    Data.UiTheme.cycle settings.uiTheme
 | 
			
		||||
 | 
			
		||||
                newSettings =
 | 
			
		||||
                    { settings | uiTheme = next }
 | 
			
		||||
            in
 | 
			
		||||
            case model.flags.account of
 | 
			
		||||
                Just _ ->
 | 
			
		||||
                    -- when authenticated, store it in settings only
 | 
			
		||||
                    -- once new settings arrive via a subscription,
 | 
			
		||||
                    -- the ui is updated. so it is also updated on
 | 
			
		||||
                    -- page refresh
 | 
			
		||||
                    ( { model | userMenuOpen = False }
 | 
			
		||||
                    , Ports.storeUiSettings model.flags newSettings
 | 
			
		||||
                    , Sub.none
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    -- when not logged in, simply set the theme
 | 
			
		||||
                    ( { model | userMenuOpen = False }
 | 
			
		||||
                    , Ports.setUiTheme next
 | 
			
		||||
                    , Sub.none
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        HomeMsg lm ->
 | 
			
		||||
            updateHome lm model
 | 
			
		||||
 | 
			
		||||
@@ -213,8 +246,17 @@ updateWithSub msg model =
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        GetUiSettings settings ->
 | 
			
		||||
            let
 | 
			
		||||
                setTheme =
 | 
			
		||||
                    Ports.setUiTheme settings.uiTheme
 | 
			
		||||
            in
 | 
			
		||||
            Util.Update.andThen2
 | 
			
		||||
                [ updateUserSettings Page.UserSettings.Data.UpdateSettings
 | 
			
		||||
                [ \m ->
 | 
			
		||||
                    ( { m | sidebarVisible = settings.sideMenuVisible }
 | 
			
		||||
                    , setTheme
 | 
			
		||||
                    , Sub.none
 | 
			
		||||
                    )
 | 
			
		||||
                , updateUserSettings Page.UserSettings.Data.UpdateSettings
 | 
			
		||||
                , updateHome Page.Home.Data.UiSettingsUpdated
 | 
			
		||||
                , updateItemDetail Page.ItemDetail.Data.UiSettingsUpdated
 | 
			
		||||
                ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										465
									
								
								modules/webapp/src/main/elm/App/View2.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								modules/webapp/src/main/elm/App/View2.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,465 @@
 | 
			
		||||
module App.View2 exposing (view)
 | 
			
		||||
 | 
			
		||||
import Api.Model.AuthResult exposing (AuthResult)
 | 
			
		||||
import App.Data exposing (..)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Page.CollectiveSettings.View2 as CollectiveSettings
 | 
			
		||||
import Page.Home.Data
 | 
			
		||||
import Page.Home.View2 as Home
 | 
			
		||||
import Page.ItemDetail.View2 as ItemDetail
 | 
			
		||||
import Page.Login.View2 as Login
 | 
			
		||||
import Page.ManageData.View2 as ManageData
 | 
			
		||||
import Page.NewInvite.View2 as NewInvite
 | 
			
		||||
import Page.Queue.View2 as Queue
 | 
			
		||||
import Page.Register.View2 as Register
 | 
			
		||||
import Page.Upload.View2 as Upload
 | 
			
		||||
import Page.UserSettings.View2 as UserSettings
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List (Html Msg)
 | 
			
		||||
view model =
 | 
			
		||||
    [ topNavbar model
 | 
			
		||||
    , mainContent model
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
topNavbar : Model -> Html Msg
 | 
			
		||||
topNavbar model =
 | 
			
		||||
    case model.flags.account of
 | 
			
		||||
        Just acc ->
 | 
			
		||||
            topNavUser acc model
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            topNavAnon model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
topNavUser : AuthResult -> Model -> Html Msg
 | 
			
		||||
topNavUser auth model =
 | 
			
		||||
    nav
 | 
			
		||||
        [ id "top-nav"
 | 
			
		||||
        , class styleTopNav
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.genericButton
 | 
			
		||||
            { label = ""
 | 
			
		||||
            , icon = "fa fa-bars"
 | 
			
		||||
            , handler = onClick ToggleSidebar
 | 
			
		||||
            , disabled = not (Page.hasSidebar model.page)
 | 
			
		||||
            , attrs = [ href "#" ]
 | 
			
		||||
            , baseStyle = "font-bold inline-flex items-center px-4 py-2"
 | 
			
		||||
            , activeStyle = "hover:bg-blue-200 dark:hover:bg-bluegray-800 w-12"
 | 
			
		||||
            }
 | 
			
		||||
        , headerNavItem model
 | 
			
		||||
        , div [ class "flex flex-grow justify-end" ]
 | 
			
		||||
            [ userMenu auth model
 | 
			
		||||
            , dataMenu auth model
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
topNavAnon : Model -> Html Msg
 | 
			
		||||
topNavAnon model =
 | 
			
		||||
    nav
 | 
			
		||||
        [ id "top-nav"
 | 
			
		||||
        , class styleTopNav
 | 
			
		||||
        ]
 | 
			
		||||
        [ headerNavItem model
 | 
			
		||||
        , div [ class "flex flex-grow justify-end" ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , onClick ToggleDarkMode
 | 
			
		||||
                , class dropdownLink
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-adjust w-6" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
headerNavItem : Model -> Html Msg
 | 
			
		||||
headerNavItem model =
 | 
			
		||||
    a
 | 
			
		||||
        [ class "font-bold hover:bg-blue-200 dark:hover:bg-bluegray-800 w-40 inline-flex items-center px-4"
 | 
			
		||||
        , Page.href HomePage
 | 
			
		||||
        ]
 | 
			
		||||
        [ img
 | 
			
		||||
            [ src (model.flags.config.docspellAssetPath ++ "/img/logo-96.png")
 | 
			
		||||
            , class "h-full inline-block py-2 pr-2"
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ text "Docspell"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mainContent : Model -> Html Msg
 | 
			
		||||
mainContent model =
 | 
			
		||||
    div
 | 
			
		||||
        [ id "main"
 | 
			
		||||
        , class styleMain
 | 
			
		||||
        ]
 | 
			
		||||
        (case model.page of
 | 
			
		||||
            HomePage ->
 | 
			
		||||
                viewHome model
 | 
			
		||||
 | 
			
		||||
            CollectiveSettingPage ->
 | 
			
		||||
                viewCollectiveSettings model
 | 
			
		||||
 | 
			
		||||
            LoginPage _ ->
 | 
			
		||||
                viewLogin model
 | 
			
		||||
 | 
			
		||||
            ManageDataPage ->
 | 
			
		||||
                viewManageData model
 | 
			
		||||
 | 
			
		||||
            UserSettingPage ->
 | 
			
		||||
                viewUserSettings model
 | 
			
		||||
 | 
			
		||||
            QueuePage ->
 | 
			
		||||
                viewQueue model
 | 
			
		||||
 | 
			
		||||
            RegisterPage ->
 | 
			
		||||
                viewRegister model
 | 
			
		||||
 | 
			
		||||
            UploadPage mid ->
 | 
			
		||||
                viewUpload mid model
 | 
			
		||||
 | 
			
		||||
            NewInvitePage ->
 | 
			
		||||
                viewNewInvite model
 | 
			
		||||
 | 
			
		||||
            ItemDetailPage id ->
 | 
			
		||||
                viewItemDetail id model
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
styleTopNav : String
 | 
			
		||||
styleTopNav =
 | 
			
		||||
    "top-0 fixed z-50 w-full flex flex-row justify-start shadow-sm h-12 bg-blue-100 dark:bg-bluegray-900 text-gray-800 dark:text-bluegray-200 antialiased"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
styleMain : String
 | 
			
		||||
styleMain =
 | 
			
		||||
    "mt-12 flex md:flex-row flex-col w-full h-screen-12 overflow-y-hidden bg-white dark:bg-bluegray-800 text-gray-800 dark:text-bluegray-300 antialiased"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dataMenu : AuthResult -> Model -> Html Msg
 | 
			
		||||
dataMenu acc model =
 | 
			
		||||
    div [ class "relative" ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ class dropdownLink
 | 
			
		||||
            , onClick ToggleNavMenu
 | 
			
		||||
            , href "#"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-cogs" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class dropdownMenu
 | 
			
		||||
            , classList [ ( "hidden", not model.navMenuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ dataPageLink model
 | 
			
		||||
                HomePage
 | 
			
		||||
                []
 | 
			
		||||
                [ img
 | 
			
		||||
                    [ class "w-4 inline-block"
 | 
			
		||||
                    , src (model.flags.config.docspellAssetPath ++ "/img/logo-mc-96.png")
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , div [ class "inline-block ml-2" ]
 | 
			
		||||
                    [ text "Items"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "py-1" ] [ hr [ class S.border ] [] ]
 | 
			
		||||
            , dataPageLink model
 | 
			
		||||
                ManageDataPage
 | 
			
		||||
                []
 | 
			
		||||
                [ i [ class "fa fa-cubes w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Manage Data"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "divider" ] []
 | 
			
		||||
            , dataPageLink model
 | 
			
		||||
                (UploadPage Nothing)
 | 
			
		||||
                []
 | 
			
		||||
                [ i [ class "fa fa-upload w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Upload files"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , dataPageLink model
 | 
			
		||||
                QueuePage
 | 
			
		||||
                []
 | 
			
		||||
                [ i [ class "fa fa-tachometer-alt w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Processing Queue"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "py-1", True )
 | 
			
		||||
                    , ( "hidden", model.flags.config.signupMode /= "invite" )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ hr [ class S.border ] [] ]
 | 
			
		||||
            , dataPageLink model
 | 
			
		||||
                NewInvitePage
 | 
			
		||||
                [ ( "hidden", model.flags.config.signupMode /= "invite" ) ]
 | 
			
		||||
                [ i [ class "fa fa-key w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "New Invites"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "py-1" ]
 | 
			
		||||
                [ hr [ class S.border ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , a
 | 
			
		||||
                [ class dropdownItem
 | 
			
		||||
                , href "https://docspell.org/docs"
 | 
			
		||||
                , target "_new"
 | 
			
		||||
                , title "Opens https://docspell.org/docs"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-question-circle w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ] [ text "Help" ]
 | 
			
		||||
                , span [ class "float-right" ]
 | 
			
		||||
                    [ i [ class "fa fa-external-link-alt w-6" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
userMenu : AuthResult -> Model -> Html Msg
 | 
			
		||||
userMenu acc model =
 | 
			
		||||
    div [ class "relative" ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ class dropdownLink
 | 
			
		||||
            , onClick ToggleUserMenu
 | 
			
		||||
            , href "#"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-user w-6" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class dropdownMenu
 | 
			
		||||
            , classList [ ( "hidden", not model.userMenuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class dropdownHeadItem ]
 | 
			
		||||
                [ i [ class "fa fa-user pr-2 font-thin" ] []
 | 
			
		||||
                , span [ class "ml-3 text-sm" ]
 | 
			
		||||
                    [ Data.Flags.accountString acc |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "py-1" ] [ hr [ class S.border ] [] ]
 | 
			
		||||
            , userPageLink model
 | 
			
		||||
                CollectiveSettingPage
 | 
			
		||||
                [ i [ class "fa fa-users w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Collective Profile"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , userPageLink model
 | 
			
		||||
                UserSettingPage
 | 
			
		||||
                [ i [ class "fa fa-user-circle w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "User Profile"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , onClick ToggleDarkMode
 | 
			
		||||
                , class dropdownItem
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-adjust w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Light/Dark"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "py-1" ] [ hr [ class S.border ] [] ]
 | 
			
		||||
            , a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class dropdownItem
 | 
			
		||||
                , onClick Logout
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-sign-out-alt w-6" ] []
 | 
			
		||||
                , span [ class "ml-1" ]
 | 
			
		||||
                    [ text "Logout"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
userPageLink : Model -> Page -> List (Html Msg) -> Html Msg
 | 
			
		||||
userPageLink model page children =
 | 
			
		||||
    a
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( dropdownItem, True )
 | 
			
		||||
            , ( "bg-gray-200 dark:bg-bluegray-700", model.page == page )
 | 
			
		||||
            ]
 | 
			
		||||
        , onClick ToggleUserMenu
 | 
			
		||||
        , Page.href page
 | 
			
		||||
        ]
 | 
			
		||||
        children
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dataPageLink : Model -> Page -> List ( String, Bool ) -> List (Html Msg) -> Html Msg
 | 
			
		||||
dataPageLink model page classes children =
 | 
			
		||||
    a
 | 
			
		||||
        [ classList
 | 
			
		||||
            ([ ( dropdownItem, True )
 | 
			
		||||
             , ( "bg-gray-200 dark:bg-bluegray-700", model.page == page )
 | 
			
		||||
             ]
 | 
			
		||||
                ++ classes
 | 
			
		||||
            )
 | 
			
		||||
        , onClick ToggleNavMenu
 | 
			
		||||
        , Page.href page
 | 
			
		||||
        ]
 | 
			
		||||
        children
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dropdownLink : String
 | 
			
		||||
dropdownLink =
 | 
			
		||||
    "px-4 py-2 w-12 font-bold inline-flex h-full items-center hover:bg-blue-200 dark:hover:bg-bluegray-800"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dropdownItem : String
 | 
			
		||||
dropdownItem =
 | 
			
		||||
    "transition-colors duration-200 items-center block px-4 py-2 text-normal hover:bg-gray-200 dark:hover:bg-bluegray-700 dark:hover:text-bluegray-50"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dropdownHeadItem : String
 | 
			
		||||
dropdownHeadItem =
 | 
			
		||||
    "transition-colors duration-200 items-center block px-4 py-2 font-semibold uppercase"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dropdownMenu : String
 | 
			
		||||
dropdownMenu =
 | 
			
		||||
    " absolute right-0 bg-white dark:bg-bluegray-800 border dark:border-bluegray-700 dark:text-bluegray-300 shadow-lg opacity-1 transition duration-200 min-w-max "
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewHome : Model -> List (Html Msg)
 | 
			
		||||
viewHome model =
 | 
			
		||||
    [ Html.map HomeMsg (Home.viewSidebar model.sidebarVisible model.flags model.uiSettings model.homeModel)
 | 
			
		||||
    , Html.map HomeMsg (Home.viewContent model.flags model.uiSettings model.homeModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCollectiveSettings : Model -> List (Html Msg)
 | 
			
		||||
viewCollectiveSettings model =
 | 
			
		||||
    [ Html.map CollSettingsMsg
 | 
			
		||||
        (CollectiveSettings.viewSidebar model.sidebarVisible
 | 
			
		||||
            model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.collSettingsModel
 | 
			
		||||
        )
 | 
			
		||||
    , Html.map CollSettingsMsg
 | 
			
		||||
        (CollectiveSettings.viewContent model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.collSettingsModel
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewLogin : Model -> List (Html Msg)
 | 
			
		||||
viewLogin model =
 | 
			
		||||
    [ Html.map LoginMsg
 | 
			
		||||
        (Login.viewSidebar model.sidebarVisible model.flags model.uiSettings model.loginModel)
 | 
			
		||||
    , Html.map LoginMsg
 | 
			
		||||
        (Login.viewContent model.flags model.uiSettings model.loginModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewManageData : Model -> List (Html Msg)
 | 
			
		||||
viewManageData model =
 | 
			
		||||
    [ Html.map ManageDataMsg
 | 
			
		||||
        (ManageData.viewSidebar model.sidebarVisible model.flags model.uiSettings model.manageDataModel)
 | 
			
		||||
    , Html.map ManageDataMsg
 | 
			
		||||
        (ManageData.viewContent model.flags model.uiSettings model.manageDataModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewUserSettings : Model -> List (Html Msg)
 | 
			
		||||
viewUserSettings model =
 | 
			
		||||
    [ Html.map UserSettingsMsg
 | 
			
		||||
        (UserSettings.viewSidebar model.sidebarVisible model.flags model.uiSettings model.userSettingsModel)
 | 
			
		||||
    , Html.map UserSettingsMsg
 | 
			
		||||
        (UserSettings.viewContent model.flags model.uiSettings model.userSettingsModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewQueue : Model -> List (Html Msg)
 | 
			
		||||
viewQueue model =
 | 
			
		||||
    [ Html.map QueueMsg
 | 
			
		||||
        (Queue.viewSidebar model.sidebarVisible model.flags model.uiSettings model.queueModel)
 | 
			
		||||
    , Html.map QueueMsg
 | 
			
		||||
        (Queue.viewContent model.flags model.uiSettings model.queueModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewRegister : Model -> List (Html Msg)
 | 
			
		||||
viewRegister model =
 | 
			
		||||
    [ Html.map RegisterMsg
 | 
			
		||||
        (Register.viewSidebar model.sidebarVisible model.flags model.uiSettings model.registerModel)
 | 
			
		||||
    , Html.map RegisterMsg
 | 
			
		||||
        (Register.viewContent model.flags model.uiSettings model.registerModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewNewInvite : Model -> List (Html Msg)
 | 
			
		||||
viewNewInvite model =
 | 
			
		||||
    [ Html.map NewInviteMsg
 | 
			
		||||
        (NewInvite.viewSidebar model.sidebarVisible model.flags model.uiSettings model.newInviteModel)
 | 
			
		||||
    , Html.map NewInviteMsg
 | 
			
		||||
        (NewInvite.viewContent model.flags model.uiSettings model.newInviteModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewUpload : Maybe String -> Model -> List (Html Msg)
 | 
			
		||||
viewUpload mid model =
 | 
			
		||||
    [ Html.map UploadMsg
 | 
			
		||||
        (Upload.viewSidebar
 | 
			
		||||
            mid
 | 
			
		||||
            model.sidebarVisible
 | 
			
		||||
            model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.uploadModel
 | 
			
		||||
        )
 | 
			
		||||
    , Html.map UploadMsg
 | 
			
		||||
        (Upload.viewContent mid
 | 
			
		||||
            model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.uploadModel
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItemDetail : String -> Model -> List (Html Msg)
 | 
			
		||||
viewItemDetail id model =
 | 
			
		||||
    let
 | 
			
		||||
        inav =
 | 
			
		||||
            Page.Home.Data.itemNav id model.homeModel
 | 
			
		||||
    in
 | 
			
		||||
    [ Html.map ItemDetailMsg
 | 
			
		||||
        (ItemDetail.viewSidebar
 | 
			
		||||
            model.sidebarVisible
 | 
			
		||||
            model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.itemDetailModel
 | 
			
		||||
        )
 | 
			
		||||
    , Html.map ItemDetailMsg
 | 
			
		||||
        (ItemDetail.viewContent
 | 
			
		||||
            inav
 | 
			
		||||
            model.flags
 | 
			
		||||
            model.uiSettings
 | 
			
		||||
            model.itemDetailModel
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
@@ -5,14 +5,17 @@ module Comp.AddressForm exposing
 | 
			
		||||
    , getAddress
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Address exposing (Address)
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.List
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -150,3 +153,81 @@ view settings model =
 | 
			
		||||
            , Html.map CountryMsg (Comp.Dropdown.view settings model.country)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "mb-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "street"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Street"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetStreet
 | 
			
		||||
                , placeholder "Street"
 | 
			
		||||
                , value model.street
 | 
			
		||||
                , name "street"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "mb-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "zip"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Zip Code"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetZip
 | 
			
		||||
                , placeholder "Zip"
 | 
			
		||||
                , value model.zip
 | 
			
		||||
                , name "zip"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "mb-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "city"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "City"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetCity
 | 
			
		||||
                , placeholder "City"
 | 
			
		||||
                , value model.city
 | 
			
		||||
                , name "city"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Country"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map CountryMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.country
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,19 @@ module Comp.AttachmentMeta exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.AttachmentMeta exposing (AttachmentMeta)
 | 
			
		||||
import Api.Model.ItemProposals exposing (ItemProposals)
 | 
			
		||||
import Api.Model.Label exposing (Label)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
@@ -58,6 +61,10 @@ update msg model =
 | 
			
		||||
            { model | meta = Failure (Util.Http.errorToString err) }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    div []
 | 
			
		||||
@@ -207,3 +214,156 @@ renderLabel label =
 | 
			
		||||
            , String.fromInt label.endPos |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ h3 [ class S.header3 ]
 | 
			
		||||
            [ text "Extracted Meta Data"
 | 
			
		||||
            ]
 | 
			
		||||
        , case model.meta of
 | 
			
		||||
            NotAvailable ->
 | 
			
		||||
                B.loadingDimmer True
 | 
			
		||||
 | 
			
		||||
            Failure msg ->
 | 
			
		||||
                div [ class S.errorMessage ]
 | 
			
		||||
                    [ text msg
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
            Success data ->
 | 
			
		||||
                viewData2 data
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewData2 : AttachmentMeta -> Html Msg
 | 
			
		||||
viewData2 meta =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "text-lg font-bold" ]
 | 
			
		||||
            [ text "Content"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "px-2 py-2 text-sm bg-yellow-50 dark:bg-warmgray-800 break-words whitespace-pre max-h-80 overflow-auto" ]
 | 
			
		||||
            [ text meta.content
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "text-lg font-bold mt-2" ]
 | 
			
		||||
            [ text "Labels"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex fex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.map renderLabelItem2 meta.labels)
 | 
			
		||||
        , div [ class "text-lg font-bold mt-2" ]
 | 
			
		||||
            [ text "Proposals"
 | 
			
		||||
            ]
 | 
			
		||||
        , viewProposals2 meta.proposals
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewProposals2 : ItemProposals -> Html Msg
 | 
			
		||||
viewProposals2 props =
 | 
			
		||||
    let
 | 
			
		||||
        mkItem n lbl =
 | 
			
		||||
            div
 | 
			
		||||
                [ class S.basicLabel
 | 
			
		||||
                , class "text-sm"
 | 
			
		||||
                ]
 | 
			
		||||
                [ text lbl.name
 | 
			
		||||
                , div [ class "opacity-75 ml-2" ]
 | 
			
		||||
                    [ (String.fromInt (n + 1) ++ ".")
 | 
			
		||||
                        |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        mkTimeItem ms =
 | 
			
		||||
            div
 | 
			
		||||
                [ class S.basicLabel
 | 
			
		||||
                , class "text-sm"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Util.Time.formatDateShort ms |> text
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "font-bold mb-2" ]
 | 
			
		||||
            [ text "Correspondent Organization"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.indexedMap mkItem props.corrOrg)
 | 
			
		||||
        , div [ class "font-bold mt-3 mb-2" ]
 | 
			
		||||
            [ text "Correspondent Person"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.indexedMap mkItem props.corrPerson)
 | 
			
		||||
        , div [ class "font-bold mt-3 mb-2" ]
 | 
			
		||||
            [ text "Concerning Person"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.indexedMap mkItem props.concPerson)
 | 
			
		||||
        , div [ class "font-bold mt-3 mb-2" ]
 | 
			
		||||
            [ text "Concerning Equipment"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.indexedMap mkItem props.concEquipment)
 | 
			
		||||
        , div [ class "font-bold mb-2 mt-3" ]
 | 
			
		||||
            [ text "Item Date"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2" ]
 | 
			
		||||
            (List.map mkTimeItem props.itemDate)
 | 
			
		||||
        , div [ class "font-bold mt-3 mb-2" ]
 | 
			
		||||
            [ text "Item Due Date"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row flex-wrap space-x-2 mb-2" ]
 | 
			
		||||
            (List.map mkTimeItem props.dueDate)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderLabelItem2 : Label -> Html Msg
 | 
			
		||||
renderLabelItem2 label =
 | 
			
		||||
    renderLabel2 label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderLabel2 : Label -> Html Msg
 | 
			
		||||
renderLabel2 label =
 | 
			
		||||
    let
 | 
			
		||||
        icon =
 | 
			
		||||
            case label.labelType of
 | 
			
		||||
                "organization" ->
 | 
			
		||||
                    "fa fa-industry"
 | 
			
		||||
 | 
			
		||||
                "person" ->
 | 
			
		||||
                    "fa fa-user"
 | 
			
		||||
 | 
			
		||||
                "location" ->
 | 
			
		||||
                    "fa fa-map-marker"
 | 
			
		||||
 | 
			
		||||
                "date" ->
 | 
			
		||||
                    "fa fa-calendar-alt"
 | 
			
		||||
 | 
			
		||||
                "misc" ->
 | 
			
		||||
                    "fa fa-question"
 | 
			
		||||
 | 
			
		||||
                "email" ->
 | 
			
		||||
                    "fa fa-at"
 | 
			
		||||
 | 
			
		||||
                "website" ->
 | 
			
		||||
                    "fa fa-external-alt"
 | 
			
		||||
 | 
			
		||||
                _ ->
 | 
			
		||||
                    "fa fa-tag"
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class S.basicLabel
 | 
			
		||||
        , class "mt-1 text-sm"
 | 
			
		||||
        , title label.labelType
 | 
			
		||||
        ]
 | 
			
		||||
        [ i [ class icon ] []
 | 
			
		||||
        , span [ class "ml-2" ]
 | 
			
		||||
            [ text label.label
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "opacity-75 ml-3" ]
 | 
			
		||||
            [ String.fromInt label.beginPos |> text
 | 
			
		||||
            , text "-"
 | 
			
		||||
            , String.fromInt label.endPos |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										286
									
								
								modules/webapp/src/main/elm/Comp/Basic.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								modules/webapp/src/main/elm/Comp/Basic.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
module Comp.Basic exposing
 | 
			
		||||
    ( editLinkLabel
 | 
			
		||||
    , editLinkTableCell
 | 
			
		||||
    , genericButton
 | 
			
		||||
    , horizontalDivider
 | 
			
		||||
    , inputRequired
 | 
			
		||||
    , linkLabel
 | 
			
		||||
    , loadingDimmer
 | 
			
		||||
    , primaryBasicButton
 | 
			
		||||
    , primaryButton
 | 
			
		||||
    , secondaryBasicButton
 | 
			
		||||
    , secondaryButton
 | 
			
		||||
    , stats
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
primaryButton :
 | 
			
		||||
    { x
 | 
			
		||||
        | label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , disabled : Bool
 | 
			
		||||
        , handler : Attribute msg
 | 
			
		||||
        , attrs : List (Attribute msg)
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
primaryButton model =
 | 
			
		||||
    genericButton
 | 
			
		||||
        { label = model.label
 | 
			
		||||
        , icon = model.icon
 | 
			
		||||
        , handler = model.handler
 | 
			
		||||
        , disabled = model.disabled
 | 
			
		||||
        , attrs = model.attrs
 | 
			
		||||
        , baseStyle = S.primaryButtonMain ++ S.primaryButtonRounded
 | 
			
		||||
        , activeStyle = S.primaryButtonHover
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
primaryBasicButton :
 | 
			
		||||
    { x
 | 
			
		||||
        | label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , disabled : Bool
 | 
			
		||||
        , handler : Attribute msg
 | 
			
		||||
        , attrs : List (Attribute msg)
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
primaryBasicButton model =
 | 
			
		||||
    genericButton
 | 
			
		||||
        { label = model.label
 | 
			
		||||
        , icon = model.icon
 | 
			
		||||
        , handler = model.handler
 | 
			
		||||
        , disabled = model.disabled
 | 
			
		||||
        , attrs = model.attrs
 | 
			
		||||
        , baseStyle = S.primaryBasicButtonMain
 | 
			
		||||
        , activeStyle = S.primaryBasicButtonHover
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
secondaryButton :
 | 
			
		||||
    { x
 | 
			
		||||
        | label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , disabled : Bool
 | 
			
		||||
        , handler : Attribute msg
 | 
			
		||||
        , attrs : List (Attribute msg)
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
secondaryButton model =
 | 
			
		||||
    genericButton
 | 
			
		||||
        { label = model.label
 | 
			
		||||
        , icon = model.icon
 | 
			
		||||
        , handler = model.handler
 | 
			
		||||
        , disabled = model.disabled
 | 
			
		||||
        , attrs = model.attrs
 | 
			
		||||
        , baseStyle = S.secondaryButtonMain ++ S.secondaryButton
 | 
			
		||||
        , activeStyle = S.secondaryButtonHover
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
secondaryBasicButton :
 | 
			
		||||
    { x
 | 
			
		||||
        | label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , disabled : Bool
 | 
			
		||||
        , handler : Attribute msg
 | 
			
		||||
        , attrs : List (Attribute msg)
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
secondaryBasicButton model =
 | 
			
		||||
    genericButton
 | 
			
		||||
        { label = model.label
 | 
			
		||||
        , icon = model.icon
 | 
			
		||||
        , handler = model.handler
 | 
			
		||||
        , disabled = model.disabled
 | 
			
		||||
        , attrs = model.attrs
 | 
			
		||||
        , baseStyle = S.secondaryBasicButtonMain ++ S.secondaryBasicButtonRounded
 | 
			
		||||
        , activeStyle = S.secondaryBasicButtonHover
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
genericButton :
 | 
			
		||||
    { x
 | 
			
		||||
        | label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , disabled : Bool
 | 
			
		||||
        , handler : Attribute msg
 | 
			
		||||
        , attrs : List (Attribute msg)
 | 
			
		||||
        , baseStyle : String
 | 
			
		||||
        , activeStyle : String
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
genericButton model =
 | 
			
		||||
    let
 | 
			
		||||
        attrs =
 | 
			
		||||
            if model.disabled then
 | 
			
		||||
                [ class model.baseStyle
 | 
			
		||||
                , class "disabled"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                    ++ model.attrs
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                [ class model.baseStyle
 | 
			
		||||
                , class model.activeStyle
 | 
			
		||||
                , model.handler
 | 
			
		||||
                ]
 | 
			
		||||
                    ++ model.attrs
 | 
			
		||||
    in
 | 
			
		||||
    genericLink model.icon model.label attrs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
linkLabel :
 | 
			
		||||
    { x
 | 
			
		||||
        | disabled : Bool
 | 
			
		||||
        , label : String
 | 
			
		||||
        , icon : String
 | 
			
		||||
        , handler : msg
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
linkLabel model =
 | 
			
		||||
    let
 | 
			
		||||
        styles =
 | 
			
		||||
            [ class S.basicLabel
 | 
			
		||||
            , class "inline-block md:text-sm my-auto whitespace-nowrap"
 | 
			
		||||
            , class "border-blue-500 text-blue-500 "
 | 
			
		||||
            , class "dark:border-lightblue-300 dark:text-lightblue-300"
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        hover =
 | 
			
		||||
            [ class "hover:bg-blue-500 hover:text-gray-200"
 | 
			
		||||
            , class "dark:hover:bg-lightblue-300 dark:hover:text-bluegray-900"
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        attrs =
 | 
			
		||||
            if model.disabled then
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class "disabled"
 | 
			
		||||
                ]
 | 
			
		||||
                    ++ styles
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                [ onClick model.handler
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                    ++ styles
 | 
			
		||||
                    ++ hover
 | 
			
		||||
    in
 | 
			
		||||
    genericLink model.icon model.label attrs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
loadingDimmer : Bool -> Html msg
 | 
			
		||||
loadingDimmer active =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden", not active )
 | 
			
		||||
            ]
 | 
			
		||||
        , class S.dimmer
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "text-gray-200" ]
 | 
			
		||||
            [ i [ class "fa fa-circle-notch animate-spin" ] []
 | 
			
		||||
            , span [ class "ml-2" ]
 | 
			
		||||
                [ text "Loading…"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
editLinkLabel : msg -> Html msg
 | 
			
		||||
editLinkLabel click =
 | 
			
		||||
    linkLabel
 | 
			
		||||
        { label = "Edit"
 | 
			
		||||
        , icon = "fa fa-edit"
 | 
			
		||||
        , handler = click
 | 
			
		||||
        , disabled = False
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
editLinkTableCell : msg -> Html msg
 | 
			
		||||
editLinkTableCell m =
 | 
			
		||||
    td [ class S.editLinkTableCellStyle ]
 | 
			
		||||
        [ editLinkLabel m
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
stats :
 | 
			
		||||
    { x
 | 
			
		||||
        | valueClass : String
 | 
			
		||||
        , rootClass : String
 | 
			
		||||
        , value : String
 | 
			
		||||
        , label : String
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
stats model =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col mx-6"
 | 
			
		||||
        , class model.rootClass
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "uppercase text-center"
 | 
			
		||||
            , class model.valueClass
 | 
			
		||||
            ]
 | 
			
		||||
            [ text model.value
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "text-center uppercase font-semibold" ]
 | 
			
		||||
            [ text model.label
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
horizontalDivider :
 | 
			
		||||
    { label : String
 | 
			
		||||
    , topCss : String
 | 
			
		||||
    , labelCss : String
 | 
			
		||||
    , lineColor : String
 | 
			
		||||
    }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
horizontalDivider settings =
 | 
			
		||||
    div [ class "inline-flex items-center", class settings.topCss ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "h-px flex-grow"
 | 
			
		||||
            , class settings.lineColor
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , div [ class "px-4 text-center" ]
 | 
			
		||||
            [ text settings.label
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "h-px flex-grow"
 | 
			
		||||
            , class settings.lineColor
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
inputRequired : Html msg
 | 
			
		||||
inputRequired =
 | 
			
		||||
    span [ class "ml-1 text-red-700" ]
 | 
			
		||||
        [ text "*"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
genericLink : String -> String -> List (Attribute msg) -> Html msg
 | 
			
		||||
genericLink icon label attrs =
 | 
			
		||||
    a
 | 
			
		||||
        attrs
 | 
			
		||||
        [ i
 | 
			
		||||
            [ class icon
 | 
			
		||||
            , classList [ ( "hidden", icon == "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , span
 | 
			
		||||
            [ class "ml-2"
 | 
			
		||||
            , classList [ ( "hidden", label == "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ text label
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
@@ -1,9 +1,15 @@
 | 
			
		||||
module Comp.BasicSizeField exposing (Msg, update, view)
 | 
			
		||||
module Comp.BasicSizeField exposing
 | 
			
		||||
    ( Msg
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Data.BasicSize exposing (BasicSize)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
@@ -17,6 +23,10 @@ update msg =
 | 
			
		||||
            Just bs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : String -> BasicSize -> Html Msg
 | 
			
		||||
view labelTxt current =
 | 
			
		||||
    div [ class "grouped fields" ]
 | 
			
		||||
@@ -38,3 +48,32 @@ makeField current element =
 | 
			
		||||
            , label [] [ text (Data.BasicSize.label element) ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> String -> BasicSize -> Html Msg
 | 
			
		||||
view2 classes labelTxt current =
 | 
			
		||||
    div [ class classes ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text labelTxt ]
 | 
			
		||||
        , div [ class "flex flex-col" ]
 | 
			
		||||
            (List.map (makeField2 current) Data.BasicSize.all)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeField2 : BasicSize -> BasicSize -> Html Msg
 | 
			
		||||
makeField2 current element =
 | 
			
		||||
    label [ class "inline-flex items-center" ]
 | 
			
		||||
        [ input
 | 
			
		||||
            [ type_ "radio"
 | 
			
		||||
            , checked (current == element)
 | 
			
		||||
            , onCheck (\_ -> Toggle element)
 | 
			
		||||
            , class S.radioInput
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , span [ class "ml-2" ]
 | 
			
		||||
            [ text (Data.BasicSize.label element) ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.CalEventInput exposing
 | 
			
		||||
    , initDefault
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -17,6 +18,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Time
 | 
			
		||||
@@ -124,6 +126,10 @@ update flags ev msg model =
 | 
			
		||||
            ( m, Cmd.none, Unknown event )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : String -> CalEvent -> Model -> Html Msg
 | 
			
		||||
view extraClasses ev model =
 | 
			
		||||
    let
 | 
			
		||||
@@ -265,3 +271,168 @@ view extraClasses ev model =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> CalEvent -> Model -> Html Msg
 | 
			
		||||
view2 extraClasses ev model =
 | 
			
		||||
    let
 | 
			
		||||
        yearLen =
 | 
			
		||||
            Basics.max 4 (String.length ev.year)
 | 
			
		||||
 | 
			
		||||
        otherLen str =
 | 
			
		||||
            Basics.max 2 (String.length str)
 | 
			
		||||
 | 
			
		||||
        styleInput =
 | 
			
		||||
            "border-0 border-b rounded-l-none rounded-r-none text-center px-1"
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( extraClasses, True )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex flex-col"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "flex flex-row items-center border px-2 py-2 text-center"
 | 
			
		||||
            , class S.border
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "flex flex-col space-y-2 mr-2" ]
 | 
			
		||||
                [ label
 | 
			
		||||
                    [ class S.inputLabel
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text "Weekday" ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , size
 | 
			
		||||
                        (Maybe.map otherLen ev.weekday
 | 
			
		||||
                            |> Maybe.withDefault 4
 | 
			
		||||
                        )
 | 
			
		||||
                    , Maybe.withDefault "" ev.weekday
 | 
			
		||||
                        |> value
 | 
			
		||||
                    , onInput SetWeekday
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex flex-col space-y-2 mr-2" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Year" ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , size yearLen
 | 
			
		||||
                    , value ev.year
 | 
			
		||||
                    , onInput SetYear
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mt-6 mr-2" ]
 | 
			
		||||
                [ text "–"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex flex-col space-y-2 mr-2" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Month" ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , size (otherLen ev.month)
 | 
			
		||||
                    , value ev.month
 | 
			
		||||
                    , onInput SetMonth
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mt-6" ]
 | 
			
		||||
                [ text "–"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex flex-col space-y-2 mr-4 mr-2" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Day"
 | 
			
		||||
                    ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , size (otherLen ev.day)
 | 
			
		||||
                    , value ev.day
 | 
			
		||||
                    , onInput SetDay
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex flex-col space-y-2 mr-2" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Hour" ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , size (otherLen ev.hour)
 | 
			
		||||
                    , value ev.hour
 | 
			
		||||
                    , onInput SetHour
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mt-6 mr-2" ]
 | 
			
		||||
                [ text ":"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex flex-col space-y-2" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Minute"
 | 
			
		||||
                    ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , class styleInput
 | 
			
		||||
                    , size (otherLen ev.minute)
 | 
			
		||||
                    , value ev.minute
 | 
			
		||||
                    , onInput SetMinute
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden invisible", not (isCheckError model) )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Error: "
 | 
			
		||||
            , Maybe.map .message model.checkResult
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden1"
 | 
			
		||||
                  , model.checkResult == Nothing || isCheckError model
 | 
			
		||||
                  )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "px-2 pt-4 pb-2 border-0 border-l border-b border-r bg-gray-50 dark:bg-bluegray-700"
 | 
			
		||||
            , class S.border
 | 
			
		||||
            ]
 | 
			
		||||
            [ div []
 | 
			
		||||
                [ div [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Schedule: "
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "px-12 font-mono " ]
 | 
			
		||||
                    [ Maybe.andThen .event model.checkResult
 | 
			
		||||
                        |> Maybe.withDefault ""
 | 
			
		||||
                        |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Next: "
 | 
			
		||||
                    ]
 | 
			
		||||
                , ul [ class "list-decimal list-inside text-sm" ]
 | 
			
		||||
                    (Maybe.map .next model.checkResult
 | 
			
		||||
                        |> Maybe.withDefault []
 | 
			
		||||
                        |> List.map Util.Time.formatDateTime
 | 
			
		||||
                        |> List.map (\s -> li [ class "" ] [ text s ])
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,17 +4,20 @@ module Comp.ChangePasswordForm exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.PasswordChange exposing (PasswordChange)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.PasswordInput
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +91,7 @@ validateModel model =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- Update
 | 
			
		||||
--- Update
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
 | 
			
		||||
@@ -174,7 +177,7 @@ update flags msg model =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- View
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
@@ -239,3 +242,95 @@ view model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    let
 | 
			
		||||
        currentEmpty =
 | 
			
		||||
            model.current == Nothing
 | 
			
		||||
 | 
			
		||||
        pass1Empty =
 | 
			
		||||
            model.newPass1 == Nothing
 | 
			
		||||
 | 
			
		||||
        pass2Empty =
 | 
			
		||||
            model.newPass2 == Nothing
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col space-y-4 relative" ]
 | 
			
		||||
        [ div []
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Current Password"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SetCurrent
 | 
			
		||||
                (Comp.PasswordInput.view2
 | 
			
		||||
                    model.current
 | 
			
		||||
                    currentEmpty
 | 
			
		||||
                    model.currentModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div []
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "New Password"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SetNew1
 | 
			
		||||
                (Comp.PasswordInput.view2
 | 
			
		||||
                    model.newPass1
 | 
			
		||||
                    pass1Empty
 | 
			
		||||
                    model.pass1Model
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div []
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "New Password (repeat)"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SetNew2
 | 
			
		||||
                (Comp.PasswordInput.view2
 | 
			
		||||
                    model.newPass2
 | 
			
		||||
                    pass2Empty
 | 
			
		||||
                    model.pass2Model
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.successMessage
 | 
			
		||||
            , classList [ ( "hidden", model.successMsg == "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ text model.successMsg
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.errorMessage
 | 
			
		||||
            , classList
 | 
			
		||||
                [ ( "hidden"
 | 
			
		||||
                  , List.isEmpty model.errors
 | 
			
		||||
                        || (currentEmpty && pass1Empty && pass2Empty)
 | 
			
		||||
                  )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ case model.errors of
 | 
			
		||||
                a :: [] ->
 | 
			
		||||
                    text a
 | 
			
		||||
 | 
			
		||||
                _ ->
 | 
			
		||||
                    ul [ class "list-disc" ]
 | 
			
		||||
                        (List.map (\em -> li [] [ text em ]) model.errors)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row" ]
 | 
			
		||||
            [ button
 | 
			
		||||
                [ class S.primaryButton
 | 
			
		||||
                , onClick Submit
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Submit"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.ClassifierSettingsForm exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -15,6 +16,7 @@ import Comp.Dropdown
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Data.CalEvent exposing (CalEvent)
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.ListType exposing (ListType)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -23,6 +25,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Http
 | 
			
		||||
import Markdown
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -177,6 +180,10 @@ update flags msg model =
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    let
 | 
			
		||||
@@ -228,3 +235,66 @@ Use an empty whitelist to disable auto tagging.
 | 
			
		||||
                (Comp.CalEventInput.view "" (Data.Validated.value model.schedule) model.scheduleModel)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        catListTypeItem =
 | 
			
		||||
            Comp.FixedDropdown.Item
 | 
			
		||||
                model.categoryListType
 | 
			
		||||
                (Data.ListType.label model.categoryListType)
 | 
			
		||||
    in
 | 
			
		||||
    div []
 | 
			
		||||
        [ Markdown.toHtml [ class "px-2 py-2 opacity-75" ]
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
Auto-tagging works by learning from existing documents. The more
 | 
			
		||||
documents you have correctly tagged, the better. Learning is done
 | 
			
		||||
periodically based on a schedule. You can specify tag-groups that
 | 
			
		||||
should either be used (whitelist) or not used (blacklist) for
 | 
			
		||||
learning.
 | 
			
		||||
 | 
			
		||||
Use an empty whitelist to disable auto tagging.
 | 
			
		||||
 | 
			
		||||
            """
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Is the following a blacklist or whitelist?" ]
 | 
			
		||||
            , Html.map CategoryListTypeMsg
 | 
			
		||||
                (Comp.FixedDropdown.view2 (Just catListTypeItem) model.categoryListTypeModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ case model.categoryListType of
 | 
			
		||||
                    Data.ListType.Whitelist ->
 | 
			
		||||
                        text "Include tag categories for learning"
 | 
			
		||||
 | 
			
		||||
                    Data.ListType.Blacklist ->
 | 
			
		||||
                        text "Exclude tag categories from learning"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map CategoryListMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.categoryListModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map ItemCountMsg
 | 
			
		||||
            (Comp.IntField.viewWithInfo2
 | 
			
		||||
                "The maximum number of items to learn from, order by date newest first. Use 0 to mean all."
 | 
			
		||||
                model.itemCount
 | 
			
		||||
                "mb-4"
 | 
			
		||||
                model.itemCountModel
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Schedule" ]
 | 
			
		||||
            , Html.map ScheduleMsg
 | 
			
		||||
                (Comp.CalEventInput.view2 "" (Data.Validated.value model.schedule) model.scheduleModel)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,17 @@ module Comp.CollectiveSettingsForm exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.ClassifierSettingsForm
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Language exposing (Language)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -20,6 +24,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -192,6 +197,10 @@ update flags msg model =
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view flags settings model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -328,3 +337,165 @@ renderResultMessage result =
 | 
			
		||||
            |> Maybe.withDefault ""
 | 
			
		||||
            |> text
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 flags settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "ui form error success", True )
 | 
			
		||||
            , ( "error", Maybe.map .success model.fullTextReIndexResult == Just False )
 | 
			
		||||
            , ( "success", Maybe.map .success model.fullTextReIndexResult == Just True )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex flex-col relative"
 | 
			
		||||
        ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.CustomElement <|
 | 
			
		||||
                    B.primaryButton
 | 
			
		||||
                        { handler = onClick SaveSettings
 | 
			
		||||
                        , label = "Save"
 | 
			
		||||
                        , icon = "fa fa-save"
 | 
			
		||||
                        , attrs =
 | 
			
		||||
                            [ title "Save settings"
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , disabled = getSettings model |> Data.Validated.isInvalid
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            , end = []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , h3 [ class S.header3 ]
 | 
			
		||||
            [ text "Document Language"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Document Language"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map LangDropdownMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.langModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "The language of your documents. This helps text recognition (OCR) and text analysis."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", not flags.config.integrationEnabled )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ h3
 | 
			
		||||
                [ class S.header3
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Integration Endpoint"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mb-4" ]
 | 
			
		||||
                [ label
 | 
			
		||||
                    [ class "inline-flex items-center"
 | 
			
		||||
                    , for "intendpoint-enabled"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ input
 | 
			
		||||
                        [ type_ "checkbox"
 | 
			
		||||
                        , onCheck (\_ -> ToggleIntegrationEndpoint)
 | 
			
		||||
                        , checked model.intEnabled
 | 
			
		||||
                        , id "intendpoint-enabled"
 | 
			
		||||
                        , class S.checkboxInput
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    , span [ class "ml-2" ]
 | 
			
		||||
                        [ text "Enable integration endpoint"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "The integration endpoint allows (local) applications to submit files. "
 | 
			
		||||
                    , text "You can choose to disable it for your collective."
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", not flags.config.fullTextSearchEnabled )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ h3
 | 
			
		||||
                [ class S.header3 ]
 | 
			
		||||
                [ text "Full-Text Search" ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ class "mb-4" ]
 | 
			
		||||
                [ div [ class "flex flex-row" ]
 | 
			
		||||
                    [ input
 | 
			
		||||
                        [ type_ "text"
 | 
			
		||||
                        , value model.fullTextConfirmText
 | 
			
		||||
                        , onInput SetFullTextConfirm
 | 
			
		||||
                        , class S.textInput
 | 
			
		||||
                        , class "rounded-r-none"
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ class S.primaryButtonPlain
 | 
			
		||||
                        , class "rouded-r"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , onClick TriggerReIndex
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-sync-alt" ] []
 | 
			
		||||
                        , span [ class "ml-2 hidden sm:inline" ]
 | 
			
		||||
                            [ text "Re-Index All Data"
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "This starts a task that clears the full-text index and re-indexes all your data again."
 | 
			
		||||
                    , text "You must type OK before clicking the button to avoid accidental re-indexing."
 | 
			
		||||
                    ]
 | 
			
		||||
                , renderResultMessage2 model.fullTextReIndexResult
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( " hidden", not flags.config.showClassificationSettings )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ h3
 | 
			
		||||
                [ class S.header3 ]
 | 
			
		||||
                [ text "Auto-Tagging"
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ class "mb-4" ]
 | 
			
		||||
                [ Html.map ClassifierSettingMsg
 | 
			
		||||
                    (Comp.ClassifierSettingsForm.view2 settings model.classifierModel)
 | 
			
		||||
                , div [ class "flex flex-row justify-end" ]
 | 
			
		||||
                    [ B.secondaryBasicButton
 | 
			
		||||
                        { handler = onClick StartClassifierTask
 | 
			
		||||
                        , icon = "fa fa-play"
 | 
			
		||||
                        , label = "Start now"
 | 
			
		||||
                        , disabled = Data.Validated.isInvalid model.classifierModel.schedule
 | 
			
		||||
                        , attrs = [ href "#" ]
 | 
			
		||||
                        }
 | 
			
		||||
                    , renderResultMessage2 model.startClassifierResult
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderResultMessage2 : Maybe BasicResult -> Html msg
 | 
			
		||||
renderResultMessage2 result =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( S.errorMessage, Maybe.map .success result == Just False )
 | 
			
		||||
            , ( S.successMessage, Maybe.map .success result == Just True )
 | 
			
		||||
            , ( "hidden", result == Nothing )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ Maybe.map .message result
 | 
			
		||||
            |> Maybe.withDefault ""
 | 
			
		||||
            |> text
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.ColorTagger exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
@@ -13,6 +14,7 @@ import Dict exposing (Dict)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -169,3 +171,92 @@ chooseColor tagger colors mtext =
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "ui labels" ] <|
 | 
			
		||||
        List.map renderLabel colors
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : FormData -> ViewOpts -> Model -> Html Msg
 | 
			
		||||
view2 data opts model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text opts.label ]
 | 
			
		||||
        , Html.map LeftMsg
 | 
			
		||||
            (Comp.FixedDropdown.view2
 | 
			
		||||
                (Maybe.map (\s -> Comp.FixedDropdown.Item s s) model.leftSelect)
 | 
			
		||||
                model.leftDropdown
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "field" ]
 | 
			
		||||
            [ chooseColor2
 | 
			
		||||
                (AddPair data)
 | 
			
		||||
                Data.Color.all
 | 
			
		||||
                Nothing
 | 
			
		||||
            ]
 | 
			
		||||
        , renderFormData2 opts data
 | 
			
		||||
        , span
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "opacity-50 text-sm", True )
 | 
			
		||||
                , ( "hidden", opts.description == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" opts.description
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderFormData2 : ViewOpts -> FormData -> Html Msg
 | 
			
		||||
renderFormData2 opts data =
 | 
			
		||||
    let
 | 
			
		||||
        values =
 | 
			
		||||
            Dict.toList data
 | 
			
		||||
 | 
			
		||||
        valueItem ( k, v ) =
 | 
			
		||||
            div [ class "flex flex-row items-center" ]
 | 
			
		||||
                [ a
 | 
			
		||||
                    [ class S.link
 | 
			
		||||
                    , class "mr-4 sm:mr-2 inline-flex"
 | 
			
		||||
                    , onClick (DeleteItem data k)
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-trash" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , a
 | 
			
		||||
                    [ class S.link
 | 
			
		||||
                    , class "mr-4 sm:mr-2 inline-flex"
 | 
			
		||||
                    , onClick (EditItem k v)
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-edit" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ opts.renderItem ( k, v )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col space-y-4 md:space-y-2 mt-2"
 | 
			
		||||
        , class "px-2 border-0 border-l dark:border-bluegray-600"
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map valueItem values)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
chooseColor2 : (Color -> msg) -> List Color -> Maybe String -> Html msg
 | 
			
		||||
chooseColor2 tagger colors mtext =
 | 
			
		||||
    let
 | 
			
		||||
        renderLabel color =
 | 
			
		||||
            a
 | 
			
		||||
                [ class (Data.Color.toString2 color)
 | 
			
		||||
                , class "label mt-1"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick (tagger color)
 | 
			
		||||
                ]
 | 
			
		||||
                [ Maybe.withDefault
 | 
			
		||||
                    (Data.Color.toString color)
 | 
			
		||||
                    mtext
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-wrap flex-row space-x-2 mt-2" ] <|
 | 
			
		||||
        List.map renderLabel colors
 | 
			
		||||
 
 | 
			
		||||
@@ -6,20 +6,24 @@ module Comp.ContactField exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view1
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Contact exposing (Contact)
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Data.ContactType exposing (ContactType)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
    { items : List Contact
 | 
			
		||||
    , kind : Comp.Dropdown.Model ContactType
 | 
			
		||||
    , kind : Comp.FixedDropdown.Model ContactType
 | 
			
		||||
    , selectedKind : Maybe ContactType
 | 
			
		||||
    , value : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -28,17 +32,8 @@ emptyModel : Model
 | 
			
		||||
emptyModel =
 | 
			
		||||
    { items = []
 | 
			
		||||
    , kind =
 | 
			
		||||
        Comp.Dropdown.makeSingleList
 | 
			
		||||
            { makeOption =
 | 
			
		||||
                \ct ->
 | 
			
		||||
                    { value = Data.ContactType.toString ct
 | 
			
		||||
                    , text = Data.ContactType.toString ct
 | 
			
		||||
                    , additional = ""
 | 
			
		||||
                    }
 | 
			
		||||
            , placeholder = ""
 | 
			
		||||
            , options = Data.ContactType.all
 | 
			
		||||
            , selected = List.head Data.ContactType.all
 | 
			
		||||
            }
 | 
			
		||||
        Comp.FixedDropdown.initMap Data.ContactType.toString Data.ContactType.all
 | 
			
		||||
    , selectedKind = List.head Data.ContactType.all
 | 
			
		||||
    , value = ""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -48,9 +43,16 @@ getContacts model =
 | 
			
		||||
    List.filter (\c -> c.value /= "") model.items
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeDropdownItem : ContactType -> Comp.FixedDropdown.Item ContactType
 | 
			
		||||
makeDropdownItem ct =
 | 
			
		||||
    { id = ct
 | 
			
		||||
    , display = Data.ContactType.toString ct
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
    = SetValue String
 | 
			
		||||
    | TypeMsg (Comp.Dropdown.Msg ContactType)
 | 
			
		||||
    | TypeMsg (Comp.FixedDropdown.Msg ContactType)
 | 
			
		||||
    | AddContact
 | 
			
		||||
    | Select Contact
 | 
			
		||||
    | SetItems (List Contact)
 | 
			
		||||
@@ -67,25 +69,36 @@ update msg model =
 | 
			
		||||
 | 
			
		||||
        TypeMsg m ->
 | 
			
		||||
            let
 | 
			
		||||
                ( m1, c1 ) =
 | 
			
		||||
                    Comp.Dropdown.update m model.kind
 | 
			
		||||
                ( m1, sel ) =
 | 
			
		||||
                    Comp.FixedDropdown.update m model.kind
 | 
			
		||||
 | 
			
		||||
                newSel =
 | 
			
		||||
                    case sel of
 | 
			
		||||
                        Just _ ->
 | 
			
		||||
                            sel
 | 
			
		||||
 | 
			
		||||
                        Nothing ->
 | 
			
		||||
                            model.selectedKind
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | kind = m1 }, Cmd.map TypeMsg c1 )
 | 
			
		||||
            ( { model | kind = m1, selectedKind = newSel }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        AddContact ->
 | 
			
		||||
            if model.value == "" then
 | 
			
		||||
                ( model, Cmd.none )
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                let
 | 
			
		||||
                    kind =
 | 
			
		||||
                        Comp.Dropdown.getSelected model.kind
 | 
			
		||||
                            |> List.head
 | 
			
		||||
                            |> Maybe.map Data.ContactType.toString
 | 
			
		||||
                in
 | 
			
		||||
                case kind of
 | 
			
		||||
                case model.selectedKind of
 | 
			
		||||
                    Just k ->
 | 
			
		||||
                        ( { model | items = Contact "" model.value k :: model.items, value = "" }
 | 
			
		||||
                        let
 | 
			
		||||
                            contact =
 | 
			
		||||
                                { id = ""
 | 
			
		||||
                                , value = model.value
 | 
			
		||||
                                , kind = Data.ContactType.toString k
 | 
			
		||||
                                }
 | 
			
		||||
                        in
 | 
			
		||||
                        ( { model | items = contact :: model.items, value = "" }
 | 
			
		||||
                        , Cmd.none
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
@@ -96,13 +109,14 @@ update msg model =
 | 
			
		||||
            let
 | 
			
		||||
                newItems =
 | 
			
		||||
                    List.filter (\c -> c /= contact) model.items
 | 
			
		||||
 | 
			
		||||
                ( m1, c1 ) =
 | 
			
		||||
                    Data.ContactType.fromString contact.kind
 | 
			
		||||
                        |> Maybe.map (\ct -> update (TypeMsg (Comp.Dropdown.SetSelection [ ct ])) model)
 | 
			
		||||
                        |> Maybe.withDefault ( model, Cmd.none )
 | 
			
		||||
            in
 | 
			
		||||
            ( { m1 | value = contact.value, items = newItems }, c1 )
 | 
			
		||||
            ( { model
 | 
			
		||||
                | value = contact.value
 | 
			
		||||
                , selectedKind = Data.ContactType.fromString contact.kind
 | 
			
		||||
                , items = newItems
 | 
			
		||||
              }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
@@ -111,7 +125,7 @@ view settings model =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view1 : UiSettings -> Bool -> Model -> Html Msg
 | 
			
		||||
view1 settings compact model =
 | 
			
		||||
view1 _ compact model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ div [ classList [ ( "fields", not compact ) ] ]
 | 
			
		||||
            [ div
 | 
			
		||||
@@ -120,7 +134,11 @@ view1 settings compact model =
 | 
			
		||||
                    , ( "four wide", not compact )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ Html.map TypeMsg (Comp.Dropdown.view settings model.kind)
 | 
			
		||||
                [ Html.map TypeMsg
 | 
			
		||||
                    (Comp.FixedDropdown.view
 | 
			
		||||
                        (Maybe.map makeDropdownItem model.selectedKind)
 | 
			
		||||
                        model.kind
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ classList
 | 
			
		||||
@@ -162,3 +180,73 @@ renderItem contact =
 | 
			
		||||
            ]
 | 
			
		||||
        , text contact.value
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Bool -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 mobile _ model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "flex flex-col space-y-2"
 | 
			
		||||
            , classList [ ( " md:flex-row md:space-y-0 md:space-x-2", not mobile ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ classList [ ( "flex-none md:w-1/6", not mobile ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ Html.map TypeMsg
 | 
			
		||||
                    (Comp.FixedDropdown.view2
 | 
			
		||||
                        (Maybe.map makeDropdownItem model.selectedKind)
 | 
			
		||||
                        model.kind
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetValue
 | 
			
		||||
                , value model.value
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , class "flex-grow"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , a
 | 
			
		||||
                [ class S.secondaryButton
 | 
			
		||||
                , class "shadow-none"
 | 
			
		||||
                , onClick AddContact
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-plus" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", List.isEmpty model.items )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "flex flex-col space-y-2 mt-2 px-2 border-0 border-l dark:border-bluegray-600 "
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map (renderItem2 mobile) model.items)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderItem2 : Bool -> Contact -> Html Msg
 | 
			
		||||
renderItem2 mobile contact =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-row space-x-2 items-center"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "mr-2 flex-nowrap" ]
 | 
			
		||||
            [ B.editLinkLabel (Select contact)
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "inline-flex items-center" ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "label inline-block mr-2 hidden text-sm "
 | 
			
		||||
                , classList [ ( " sm:inline-block", not mobile ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ text contact.kind
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "font-mono my-auto inline-block truncate" ]
 | 
			
		||||
                [ text contact.value
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -8,21 +8,26 @@ module Comp.CustomFieldForm exposing
 | 
			
		||||
    , makeField
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.NewCustomField exposing (NewCustomField)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.CustomFieldType exposing (CustomFieldType)
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Validated exposing (Validated)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -304,3 +309,157 @@ viewButtons model =
 | 
			
		||||
        [ text "Delete"
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : ViewSettings -> Model -> List (Html Msg)
 | 
			
		||||
view2 viewSettings model =
 | 
			
		||||
    let
 | 
			
		||||
        mkItem cft =
 | 
			
		||||
            Comp.FixedDropdown.Item cft (Data.CustomFieldType.label cft)
 | 
			
		||||
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this custom field?"
 | 
			
		||||
    in
 | 
			
		||||
    (if viewSettings.showControls then
 | 
			
		||||
        [ viewButtons2 model ]
 | 
			
		||||
 | 
			
		||||
     else
 | 
			
		||||
        []
 | 
			
		||||
    )
 | 
			
		||||
        ++ [ div
 | 
			
		||||
                [ class viewSettings.classes
 | 
			
		||||
                , class "flex flex-col md:relative"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Html.map DeleteMsg
 | 
			
		||||
                    (Comp.YesNoDimmer.viewN
 | 
			
		||||
                        True
 | 
			
		||||
                        dimmerSettings
 | 
			
		||||
                        model.deleteDimmer
 | 
			
		||||
                    )
 | 
			
		||||
                , if model.field.id == "" then
 | 
			
		||||
                    div [ class "py-2 text-lg opacity-75" ]
 | 
			
		||||
                        [ text "Create a new custom field."
 | 
			
		||||
                        ]
 | 
			
		||||
 | 
			
		||||
                  else
 | 
			
		||||
                    div [ class "py-2 text-lg opacity-75" ]
 | 
			
		||||
                        [ text "Note that changing the format may "
 | 
			
		||||
                        , text "result in invisible values in the ui, if they don't comply to the new format!"
 | 
			
		||||
                        ]
 | 
			
		||||
                , div [ class "mb-4" ]
 | 
			
		||||
                    [ label
 | 
			
		||||
                        [ class S.inputLabel
 | 
			
		||||
                        , for "fieldname"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ text "Name"
 | 
			
		||||
                        , B.inputRequired
 | 
			
		||||
                        ]
 | 
			
		||||
                    , input
 | 
			
		||||
                        [ type_ "text"
 | 
			
		||||
                        , onInput SetName
 | 
			
		||||
                        , model.name
 | 
			
		||||
                            |> Maybe.withDefault ""
 | 
			
		||||
                            |> value
 | 
			
		||||
                        , class S.textInput
 | 
			
		||||
                        , classList
 | 
			
		||||
                            [ ( S.inputErrorBorder, model.name == Nothing )
 | 
			
		||||
                            ]
 | 
			
		||||
                        , id "fieldname"
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    , div [ class "opacity-75 text-sm" ]
 | 
			
		||||
                        [ text "The name uniquely identifies this field. It must be a valid "
 | 
			
		||||
                        , text "identifier, not contain spaces or weird characters."
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div
 | 
			
		||||
                    [ class "mb-4"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ text "Field Format"
 | 
			
		||||
                        , B.inputRequired
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Html.map FTypeMsg
 | 
			
		||||
                        (Comp.FixedDropdown.viewStyled2
 | 
			
		||||
                            DS.mainStyle
 | 
			
		||||
                            (model.ftype == Nothing)
 | 
			
		||||
                            (Maybe.map mkItem model.ftype)
 | 
			
		||||
                            model.ftypeModel
 | 
			
		||||
                        )
 | 
			
		||||
                    , div [ class "opacity-75 text-sm" ]
 | 
			
		||||
                        [ text "A field must have a format. Values are validated "
 | 
			
		||||
                        , text "according to this format."
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "mb-4" ]
 | 
			
		||||
                    [ label
 | 
			
		||||
                        [ class S.inputLabel
 | 
			
		||||
                        , for "fieldlabel"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ text "Label" ]
 | 
			
		||||
                    , input
 | 
			
		||||
                        [ type_ "text"
 | 
			
		||||
                        , onInput SetLabel
 | 
			
		||||
                        , model.label
 | 
			
		||||
                            |> Maybe.withDefault ""
 | 
			
		||||
                            |> value
 | 
			
		||||
                        , class S.textInput
 | 
			
		||||
                        , id "fieldlabel"
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    , div [ class "opacity-75 text-sm" ]
 | 
			
		||||
                        [ text "The user defined label for this field. This is used to represent "
 | 
			
		||||
                        , text "this field in the ui. If not present, the name is used."
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( "hidden", model.result == Nothing )
 | 
			
		||||
                        , ( S.errorMessage, Maybe.map .success model.result == Just False )
 | 
			
		||||
                        , ( S.successMessage, Maybe.map .success model.result == Just True )
 | 
			
		||||
                        ]
 | 
			
		||||
                    , class "mb-4"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ Maybe.map .message model.result
 | 
			
		||||
                        |> Maybe.withDefault ""
 | 
			
		||||
                        |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
           ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewButtons2 : Model -> Html Msg
 | 
			
		||||
viewButtons2 model =
 | 
			
		||||
    MB.view
 | 
			
		||||
        { start =
 | 
			
		||||
            [ MB.PrimaryButton
 | 
			
		||||
                { tagger = SubmitForm
 | 
			
		||||
                , title = "Submit this form"
 | 
			
		||||
                , icon = Just "fa fa-save"
 | 
			
		||||
                , label = "Submit"
 | 
			
		||||
                }
 | 
			
		||||
            , MB.SecondaryButton
 | 
			
		||||
                { tagger = GoBack
 | 
			
		||||
                , title = "Back to list"
 | 
			
		||||
                , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                , label = "Cancel"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , end =
 | 
			
		||||
            if model.field.id /= "" then
 | 
			
		||||
                [ MB.DeleteButton
 | 
			
		||||
                    { tagger = RequestDelete
 | 
			
		||||
                    , title = "Delete this field"
 | 
			
		||||
                    , icon = Just "fa fa-trash"
 | 
			
		||||
                    , label = "Delete"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                []
 | 
			
		||||
        , rootClasses = "mb-4"
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,13 @@ module Comp.CustomFieldInput exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , updateSearch
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
 | 
			
		||||
import Comp.DatePicker
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.CustomFieldType exposing (CustomFieldType)
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
import Data.Money
 | 
			
		||||
@@ -21,6 +23,7 @@ import DatePicker exposing (DatePicker)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -329,11 +332,6 @@ hasWildCards msg =
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mkLabel : Model -> String
 | 
			
		||||
mkLabel model =
 | 
			
		||||
    Maybe.withDefault model.field.name model.field.label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
removeButton : String -> Html Msg
 | 
			
		||||
removeButton classes =
 | 
			
		||||
    a
 | 
			
		||||
@@ -443,9 +441,151 @@ makeInput icon model =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> Maybe String -> Model -> Html Msg
 | 
			
		||||
view2 classes icon model =
 | 
			
		||||
    let
 | 
			
		||||
        error =
 | 
			
		||||
            errorMsg model
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class classes
 | 
			
		||||
        ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ mkLabel model |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , makeInput2 icon model
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.errorMessage
 | 
			
		||||
            , class "text-sm px-2 py-1 mt-1"
 | 
			
		||||
            , classList
 | 
			
		||||
                [ ( "hidden", error == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" error |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
removeButton2 : String -> Html Msg
 | 
			
		||||
removeButton2 classes =
 | 
			
		||||
    a
 | 
			
		||||
        [ class classes
 | 
			
		||||
        , class S.inputLeftIconLinkSidebar
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , title "Remove this value"
 | 
			
		||||
        , onClick Remove
 | 
			
		||||
        ]
 | 
			
		||||
        [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeInput2 : Maybe String -> Model -> Html Msg
 | 
			
		||||
makeInput2 icon model =
 | 
			
		||||
    let
 | 
			
		||||
        iconOr c =
 | 
			
		||||
            Maybe.withDefault c icon
 | 
			
		||||
    in
 | 
			
		||||
    case model.fieldModel of
 | 
			
		||||
        TextField v ->
 | 
			
		||||
            div [ class "flex flex-row relative" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , Maybe.withDefault "" v |> value
 | 
			
		||||
                    , onInput SetText
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    , class "pl-10 pr-10"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , removeButton2 ""
 | 
			
		||||
                , i
 | 
			
		||||
                    [ class (iconOr <| Icons.customFieldType2 Data.CustomFieldType.Text)
 | 
			
		||||
                    , class S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        NumberField nm ->
 | 
			
		||||
            div [ class "flex flex-row relative" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , value nm.input
 | 
			
		||||
                    , onInput NumberMsg
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    , class "pl-10 pr-10"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , removeButton2 ""
 | 
			
		||||
                , i
 | 
			
		||||
                    [ class (iconOr <| Icons.customFieldType2 Data.CustomFieldType.Numeric)
 | 
			
		||||
                    , class S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        MoneyField nm ->
 | 
			
		||||
            div [ class "flex flex-row relative" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , value nm.input
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    , class "pl-10 pr-10"
 | 
			
		||||
                    , onInput MoneyMsg
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , removeButton2 ""
 | 
			
		||||
                , i
 | 
			
		||||
                    [ class (iconOr <| Icons.customFieldType2 Data.CustomFieldType.Money)
 | 
			
		||||
                    , class S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        BoolField b ->
 | 
			
		||||
            div [ class "flex flex-row items-center" ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { id = "customfield-flag-" ++ model.field.name
 | 
			
		||||
                        , tagger = \_ -> ToggleBool
 | 
			
		||||
                        , label = mkLabel model
 | 
			
		||||
                        , value = b
 | 
			
		||||
                        }
 | 
			
		||||
                , div [ class "flex-grow" ] []
 | 
			
		||||
                , a
 | 
			
		||||
                    [ class S.secondaryButton
 | 
			
		||||
                    , class "shadow-none"
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , title "Remove this value"
 | 
			
		||||
                    , onClick Remove
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        DateField v dp ->
 | 
			
		||||
            div [ class "flex flex-row relative" ]
 | 
			
		||||
                [ Html.map DateMsg
 | 
			
		||||
                    (Comp.DatePicker.view v Comp.DatePicker.defaultSettings dp)
 | 
			
		||||
                , removeButton2 ""
 | 
			
		||||
                , i
 | 
			
		||||
                    [ class (iconOr <| Icons.customFieldType2 Data.CustomFieldType.Date)
 | 
			
		||||
                    , class S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mkLabel : Model -> String
 | 
			
		||||
mkLabel model =
 | 
			
		||||
    Maybe.withDefault model.field.name model.field.label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
string2Float : String -> Result String Float
 | 
			
		||||
string2Float str =
 | 
			
		||||
    case String.toFloat str of
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,23 @@ module Comp.CustomFieldManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.CustomFieldList exposing (CustomFieldList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.CustomFieldForm
 | 
			
		||||
import Comp.CustomFieldTable
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.CustomField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -192,3 +197,69 @@ viewTable model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> Model -> Html Msg
 | 
			
		||||
view2 flags model =
 | 
			
		||||
    case model.detailModel of
 | 
			
		||||
        Just dm ->
 | 
			
		||||
            viewDetail2 flags dm
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            viewTable2 model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewDetail2 : Flags -> Comp.CustomFieldForm.Model -> Html Msg
 | 
			
		||||
viewDetail2 _ detailModel =
 | 
			
		||||
    let
 | 
			
		||||
        viewSettings =
 | 
			
		||||
            Comp.CustomFieldForm.fullViewSettings
 | 
			
		||||
    in
 | 
			
		||||
    div []
 | 
			
		||||
        ([ if detailModel.field.id == "" then
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new custom field"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
           else
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ Util.CustomField.nameOrLabel detailModel.field |> text
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text detailModel.field.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
         ]
 | 
			
		||||
            ++ List.map (Html.map DetailMsg) (Comp.CustomFieldForm.view2 viewSettings detailModel)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col md:relative" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewCustomField
 | 
			
		||||
                    , title = "Add a new custom field"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New custom field"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.CustomFieldTable.view2 model.tableModel model.fields)
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ module Comp.CustomFieldMultiInput exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , updateSearch
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -22,12 +23,14 @@ import Api.Model.ItemFieldValue exposing (ItemFieldValue)
 | 
			
		||||
import Comp.CustomFieldInput
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Data.CustomFieldChange exposing (CustomFieldChange(..))
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Dict exposing (Dict)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.CustomField
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -148,18 +151,18 @@ mkItem f =
 | 
			
		||||
    Comp.FixedDropdown.Item f (Maybe.withDefault f.name f.label)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : Msg -> Model -> UpdateResult
 | 
			
		||||
update : Flags -> Msg -> Model -> UpdateResult
 | 
			
		||||
update =
 | 
			
		||||
    update1 False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
updateSearch : Msg -> Model -> UpdateResult
 | 
			
		||||
updateSearch : Flags -> Msg -> Model -> UpdateResult
 | 
			
		||||
updateSearch =
 | 
			
		||||
    update1 True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update1 : Bool -> Msg -> Model -> UpdateResult
 | 
			
		||||
update1 forSearch msg model =
 | 
			
		||||
update1 : Bool -> Flags -> Msg -> Model -> UpdateResult
 | 
			
		||||
update1 forSearch flags msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
        CreateNewField ->
 | 
			
		||||
            UpdateResult model Cmd.none FieldCreateNew
 | 
			
		||||
@@ -195,7 +198,7 @@ update1 forSearch msg model =
 | 
			
		||||
            in
 | 
			
		||||
            case sel of
 | 
			
		||||
                Just field ->
 | 
			
		||||
                    update (ApplyField field) model
 | 
			
		||||
                    update flags (ApplyField field) model
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    UpdateResult model_ Cmd.none NoFieldChange
 | 
			
		||||
@@ -213,11 +216,12 @@ update1 forSearch msg model =
 | 
			
		||||
 | 
			
		||||
                -- have to re-state the open menu when this is invoked
 | 
			
		||||
                -- from a click in the dropdown
 | 
			
		||||
                -- this hack is only required for the semantic-ui version
 | 
			
		||||
                fSelectDropdown =
 | 
			
		||||
                    fSelect.dropdown
 | 
			
		||||
 | 
			
		||||
                dropdownOpen =
 | 
			
		||||
                    { fSelectDropdown | menuOpen = True }
 | 
			
		||||
                    { fSelectDropdown | menuOpen = flags.config.uiVersion /= 2 }
 | 
			
		||||
 | 
			
		||||
                model_ =
 | 
			
		||||
                    { model
 | 
			
		||||
@@ -279,7 +283,7 @@ update1 forSearch msg model =
 | 
			
		||||
                                    NoFieldChange
 | 
			
		||||
                    in
 | 
			
		||||
                    if res.result == Comp.CustomFieldInput.RemoveField then
 | 
			
		||||
                        update (RemoveField field) model_
 | 
			
		||||
                        update flags (RemoveField field) model_
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        UpdateResult model_ cmd_ result
 | 
			
		||||
@@ -327,6 +331,10 @@ type alias ViewSettings =
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : ViewSettings -> Model -> Html Msg
 | 
			
		||||
view viewSettings model =
 | 
			
		||||
    div [ class viewSettings.classes ]
 | 
			
		||||
@@ -387,3 +395,80 @@ addFieldLink classes _ =
 | 
			
		||||
        ]
 | 
			
		||||
        [ i [ class "plus link icon" ] []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : DS.DropdownStyle -> ViewSettings -> Model -> Html Msg
 | 
			
		||||
view2 ddstyle viewSettings model =
 | 
			
		||||
    div [ class viewSettings.classes ]
 | 
			
		||||
        (viewMenuBar2 ddstyle viewSettings model
 | 
			
		||||
            :: List.map (viewCustomField2 viewSettings model) (visibleFields model)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewMenuBar2 : DS.DropdownStyle -> ViewSettings -> Model -> Html Msg
 | 
			
		||||
viewMenuBar2 ddstyle viewSettings model =
 | 
			
		||||
    let
 | 
			
		||||
        { dropdown, selected } =
 | 
			
		||||
            model.fieldSelect
 | 
			
		||||
 | 
			
		||||
        ddstyleFlex =
 | 
			
		||||
            { ddstyle | root = ddstyle.root ++ " flex-grow" }
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "", viewSettings.showAddButton )
 | 
			
		||||
            ]
 | 
			
		||||
        , class " flex flex-row"
 | 
			
		||||
        ]
 | 
			
		||||
        (Html.map FieldSelectMsg
 | 
			
		||||
            (Comp.FixedDropdown.viewStyled2
 | 
			
		||||
                ddstyleFlex
 | 
			
		||||
                False
 | 
			
		||||
                (Maybe.map mkItem selected)
 | 
			
		||||
                dropdown
 | 
			
		||||
            )
 | 
			
		||||
            :: (if viewSettings.showAddButton then
 | 
			
		||||
                    [ addFieldLink2 "ml-1" model
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
               )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCustomField2 : ViewSettings -> Model -> CustomField -> Html Msg
 | 
			
		||||
viewCustomField2 viewSettings model field =
 | 
			
		||||
    let
 | 
			
		||||
        visibleField =
 | 
			
		||||
            Dict.get field.name model.visibleFields
 | 
			
		||||
    in
 | 
			
		||||
    case visibleField of
 | 
			
		||||
        Just vf ->
 | 
			
		||||
            Html.map (CustomFieldInputMsg field)
 | 
			
		||||
                (Comp.CustomFieldInput.view2 "mt-2"
 | 
			
		||||
                    (viewSettings.fieldIcon vf.field)
 | 
			
		||||
                    vf.inputModel
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            span [] []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
addFieldLink2 : String -> Model -> Html Msg
 | 
			
		||||
addFieldLink2 classes _ =
 | 
			
		||||
    a
 | 
			
		||||
        [ class classes
 | 
			
		||||
        , class S.secondaryButton
 | 
			
		||||
 | 
			
		||||
        --        , class "absolute -right-12 top-0"
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , onClick CreateNewField
 | 
			
		||||
        , title "Create a new custom field"
 | 
			
		||||
        ]
 | 
			
		||||
        [ i [ class "fa fa-plus" ] []
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.CustomFieldTable exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +42,10 @@ update msg model =
 | 
			
		||||
            ( model, EditAction item )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List CustomField -> Html Msg
 | 
			
		||||
view _ items =
 | 
			
		||||
    div []
 | 
			
		||||
@@ -86,3 +93,47 @@ viewItem item =
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> List CustomField -> Html Msg
 | 
			
		||||
view2 _ items =
 | 
			
		||||
    div []
 | 
			
		||||
        [ table [ class S.tableMain ]
 | 
			
		||||
            [ thead []
 | 
			
		||||
                [ tr []
 | 
			
		||||
                    [ th [] []
 | 
			
		||||
                    , th [ class "text-left" ] [ text "Name/Label" ]
 | 
			
		||||
                    , th [ class "text-left" ] [ text "Format" ]
 | 
			
		||||
                    , th [ class "text-center hidden sm:table-cell" ] [ text "#Usage" ]
 | 
			
		||||
                    , th [ class "text-center hidden sm:table-cell" ] [ text "Created" ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , tbody []
 | 
			
		||||
                (List.map viewItem2 items)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : CustomField -> Html Msg
 | 
			
		||||
viewItem2 item =
 | 
			
		||||
    tr [ class S.tableRow ]
 | 
			
		||||
        [ B.editLinkTableCell (EditItem item)
 | 
			
		||||
        , td [ class "text-left py-4 md:py-2 pr-2" ]
 | 
			
		||||
            [ text <| Maybe.withDefault item.name item.label
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left py-4 md:py-2 pr-2" ]
 | 
			
		||||
            [ text item.ftype
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center py-4 md:py-2 sm:pr-2 hidden sm:table-cell" ]
 | 
			
		||||
            [ String.fromInt item.usages
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center py-4 md:py-2 hidden sm:table-cell" ]
 | 
			
		||||
            [ Util.Time.formatDateShort item.created
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.DetailEdit exposing
 | 
			
		||||
    , editEquip
 | 
			
		||||
    , editOrg
 | 
			
		||||
    , editPerson
 | 
			
		||||
    , formHeading
 | 
			
		||||
    , initConcPerson
 | 
			
		||||
    , initCorrPerson
 | 
			
		||||
    , initCustomField
 | 
			
		||||
@@ -14,7 +15,9 @@ module Comp.DetailEdit exposing
 | 
			
		||||
    , initTagByName
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    , viewModal
 | 
			
		||||
    , viewModal2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
{-| Module for allowing to edit metadata in the item-edit menu.
 | 
			
		||||
@@ -32,6 +35,7 @@ import Api.Model.Organization exposing (Organization)
 | 
			
		||||
import Api.Model.Person exposing (Person)
 | 
			
		||||
import Api.Model.ReferenceList exposing (ReferenceList)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.CustomFieldForm
 | 
			
		||||
import Comp.EquipmentForm
 | 
			
		||||
import Comp.OrgForm
 | 
			
		||||
@@ -45,6 +49,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -775,3 +780,171 @@ viewModal settings mm =
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : List (Attribute Msg) -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 attr settings model =
 | 
			
		||||
    div attr
 | 
			
		||||
        (viewIntern2 settings True model)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
formHeading : String -> Model -> Html msg
 | 
			
		||||
formHeading classes model =
 | 
			
		||||
    let
 | 
			
		||||
        heading =
 | 
			
		||||
            fold (\_ -> "Add Tag")
 | 
			
		||||
                (\_ -> "Add Person")
 | 
			
		||||
                (\_ -> "Add Organization")
 | 
			
		||||
                (\_ -> "Add Equipment")
 | 
			
		||||
                (\_ -> "Add Custom Field")
 | 
			
		||||
 | 
			
		||||
        headIcon =
 | 
			
		||||
            fold (\_ -> Icons.tagIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.personIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.organizationIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.equipmentIcon2 "mt-2")
 | 
			
		||||
                (\_ -> Icons.customFieldIcon2 "mr-2")
 | 
			
		||||
    in
 | 
			
		||||
    div [ class classes ]
 | 
			
		||||
        [ headIcon model.form
 | 
			
		||||
        , text (heading model.form)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewModal2 : UiSettings -> Maybe Model -> Html Msg
 | 
			
		||||
viewModal2 settings mm =
 | 
			
		||||
    let
 | 
			
		||||
        hidden =
 | 
			
		||||
            mm == Nothing
 | 
			
		||||
 | 
			
		||||
        heading =
 | 
			
		||||
            fold (\_ -> "Add Tag")
 | 
			
		||||
                (\_ -> "Add Person")
 | 
			
		||||
                (\_ -> "Add Organization")
 | 
			
		||||
                (\_ -> "Add Equipment")
 | 
			
		||||
                (\_ -> "Add Custom Field")
 | 
			
		||||
 | 
			
		||||
        headIcon =
 | 
			
		||||
            fold (\_ -> Icons.tagIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.personIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.organizationIcon2 "mr-2")
 | 
			
		||||
                (\_ -> Icons.equipmentIcon2 "mt-2")
 | 
			
		||||
                (\_ -> Icons.customFieldIcon2 "mr-2")
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( S.dimmer, True )
 | 
			
		||||
            , ( " hidden", hidden )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class ""
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class S.header2 ]
 | 
			
		||||
                [ Maybe.map .form mm
 | 
			
		||||
                    |> Maybe.map headIcon
 | 
			
		||||
                    |> Maybe.withDefault (i [] [])
 | 
			
		||||
                , Maybe.map .form mm
 | 
			
		||||
                    |> Maybe.map heading
 | 
			
		||||
                    |> Maybe.withDefault ""
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "scrolling content" ]
 | 
			
		||||
                (case mm of
 | 
			
		||||
                    Just model ->
 | 
			
		||||
                        viewIntern2 settings False model
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        []
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "flex flex-row space-x-2" ]
 | 
			
		||||
                (case mm of
 | 
			
		||||
                    Just model ->
 | 
			
		||||
                        viewButtons2 model
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        []
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewButtons2 : Model -> List (Html Msg)
 | 
			
		||||
viewButtons2 model =
 | 
			
		||||
    [ B.primaryButton
 | 
			
		||||
        { label = "Submit"
 | 
			
		||||
        , icon =
 | 
			
		||||
            if model.submitting || model.loading then
 | 
			
		||||
                "fa fa-circle-notch animate-spin"
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                "fa fa-save"
 | 
			
		||||
        , disabled = model.submitting || model.loading
 | 
			
		||||
        , handler = onClick Submit
 | 
			
		||||
        , attrs = [ href "#" ]
 | 
			
		||||
        }
 | 
			
		||||
    , B.secondaryButton
 | 
			
		||||
        { label = "Cancel"
 | 
			
		||||
        , handler = onClick Cancel
 | 
			
		||||
        , disabled = False
 | 
			
		||||
        , icon = "fa fa-times"
 | 
			
		||||
        , attrs = [ href "#" ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewIntern2 : UiSettings -> Bool -> Model -> List (Html Msg)
 | 
			
		||||
viewIntern2 settings withButtons model =
 | 
			
		||||
    let
 | 
			
		||||
        viewSettings =
 | 
			
		||||
            Comp.CustomFieldForm.fullViewSettings
 | 
			
		||||
    in
 | 
			
		||||
    [ div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( S.errorMessage, Maybe.map .success model.result == Just False )
 | 
			
		||||
            , ( S.successMessage, Maybe.map .success model.result == Just True )
 | 
			
		||||
            , ( "hidden", model.result == Nothing )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ Maybe.map .message model.result
 | 
			
		||||
            |> Maybe.withDefault ""
 | 
			
		||||
            |> text
 | 
			
		||||
        ]
 | 
			
		||||
    , case model.form of
 | 
			
		||||
        TM tm ->
 | 
			
		||||
            Html.map TagMsg (Comp.TagForm.view2 tm)
 | 
			
		||||
 | 
			
		||||
        PMR pm ->
 | 
			
		||||
            Html.map PersonMsg (Comp.PersonForm.view2 True settings pm)
 | 
			
		||||
 | 
			
		||||
        PMC pm ->
 | 
			
		||||
            Html.map PersonMsg (Comp.PersonForm.view2 True settings pm)
 | 
			
		||||
 | 
			
		||||
        OM om ->
 | 
			
		||||
            Html.map OrgMsg (Comp.OrgForm.view2 True settings om)
 | 
			
		||||
 | 
			
		||||
        EM em ->
 | 
			
		||||
            Html.map EquipMsg (Comp.EquipmentForm.view2 em)
 | 
			
		||||
 | 
			
		||||
        CFM fm ->
 | 
			
		||||
            div []
 | 
			
		||||
                (List.map (Html.map CustomFieldMsg)
 | 
			
		||||
                    (Comp.CustomFieldForm.view2
 | 
			
		||||
                        customFieldFormSettings
 | 
			
		||||
                        fm
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
    ]
 | 
			
		||||
        ++ (if withButtons then
 | 
			
		||||
                [ div [ class "flex flex-row space-x-2" ]
 | 
			
		||||
                    (viewButtons2 model)
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                []
 | 
			
		||||
           )
 | 
			
		||||
 
 | 
			
		||||
@@ -14,13 +14,16 @@ module Comp.Dropdown exposing
 | 
			
		||||
    , setMkOption
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    , viewSingle
 | 
			
		||||
    , viewSingle2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
{-| This needs to be rewritten from scratch!
 | 
			
		||||
-}
 | 
			
		||||
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
@@ -123,7 +126,12 @@ makeSingle opts =
 | 
			
		||||
        , searchable = \n -> n > 0
 | 
			
		||||
        , makeOption = opts.makeOption
 | 
			
		||||
        , labelColor = \_ -> \_ -> ""
 | 
			
		||||
        , placeholder = opts.placeholder
 | 
			
		||||
        , placeholder =
 | 
			
		||||
            if opts.placeholder == "" then
 | 
			
		||||
                "Select…"
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                opts.placeholder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -181,6 +189,7 @@ type Msg a
 | 
			
		||||
    | ToggleMenu
 | 
			
		||||
    | AddItem (Item a)
 | 
			
		||||
    | RemoveItem (Item a)
 | 
			
		||||
    | RemoveItem2 (Item a)
 | 
			
		||||
    | Filter String
 | 
			
		||||
    | ShowMenu Bool
 | 
			
		||||
    | KeyPress Int
 | 
			
		||||
@@ -333,6 +342,9 @@ isDropdownChangeMsg cm =
 | 
			
		||||
        RemoveItem _ ->
 | 
			
		||||
            True
 | 
			
		||||
 | 
			
		||||
        RemoveItem2 _ ->
 | 
			
		||||
            True
 | 
			
		||||
 | 
			
		||||
        KeyPress code ->
 | 
			
		||||
            Util.Html.intToKeyCode code
 | 
			
		||||
                |> Maybe.map (\c -> c == Util.Html.Enter || c == Util.Html.ESC)
 | 
			
		||||
@@ -379,6 +391,16 @@ update msg model =
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        RemoveItem2 e ->
 | 
			
		||||
            let
 | 
			
		||||
                m =
 | 
			
		||||
                    deselectItem model e |> applyFilter ""
 | 
			
		||||
            in
 | 
			
		||||
            ( -- Hack above only needed with semanticui
 | 
			
		||||
              m
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        Filter str ->
 | 
			
		||||
            let
 | 
			
		||||
                m =
 | 
			
		||||
@@ -609,3 +631,180 @@ renderOption item =
 | 
			
		||||
            [ text item.option.additional
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : DS.DropdownStyle -> UiSettings -> Model a -> Html (Msg a)
 | 
			
		||||
view2 style settings model =
 | 
			
		||||
    if model.multiple then
 | 
			
		||||
        viewMultiple2 style settings model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewSingle2 style model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewSingle2 : DS.DropdownStyle -> Model a -> Html (Msg a)
 | 
			
		||||
viewSingle2 style model =
 | 
			
		||||
    let
 | 
			
		||||
        renderItem item =
 | 
			
		||||
            a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class style.item
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( style.itemActive, item.active )
 | 
			
		||||
                    , ( "font-semibold", item.selected )
 | 
			
		||||
                    ]
 | 
			
		||||
                , onClick (AddItem item)
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                ]
 | 
			
		||||
                [ text item.option.text
 | 
			
		||||
                , span [ class "text-gray-400 float-right" ]
 | 
			
		||||
                    [ text item.option.additional
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        sel =
 | 
			
		||||
            List.head model.selected
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "relative "
 | 
			
		||||
        , onKeyUp KeyPress
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class style.link
 | 
			
		||||
            ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ class "flex-grow"
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "opacity-50", sel == Nothing )
 | 
			
		||||
                    , ( "hidden", model.menuOpen && isSearchable model )
 | 
			
		||||
                    , ( "ml-4", sel /= Nothing )
 | 
			
		||||
                    ]
 | 
			
		||||
                , tabindex 0
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                , onClick ToggleMenu
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Maybe.map (.option >> .text) sel
 | 
			
		||||
                    |> Maybe.withDefault model.placeholder
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
            , a
 | 
			
		||||
                [ class "absolute left-3"
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "hidden", (model.menuOpen && isSearchable model) || sel == Nothing )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class "hover:opacity-50"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , Maybe.map (\item -> onClick (RemoveItem2 item)) sel
 | 
			
		||||
                    |> Maybe.withDefault (class "hidden")
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-times" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder model.placeholder
 | 
			
		||||
                , onInput Filter
 | 
			
		||||
                , value model.filterString
 | 
			
		||||
                , class "inline-block border-0 px-0 w-full py-0 focus:ring-0 "
 | 
			
		||||
                , class style.input
 | 
			
		||||
                , classList [ ( "hidden", not (model.menuOpen && isSearchable model) ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , a
 | 
			
		||||
                [ class "rounded cursor-pointer ml-2 absolute right-2"
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                , onClick ToggleMenu
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-angle-down px-2" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class style.menu
 | 
			
		||||
            , classList [ ( "hidden", not model.menuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            (getOptions model |> List.map renderItem)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewMultiple2 : DS.DropdownStyle -> UiSettings -> Model a -> Html (Msg a)
 | 
			
		||||
viewMultiple2 style settings model =
 | 
			
		||||
    let
 | 
			
		||||
        renderItem item =
 | 
			
		||||
            a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class style.item
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( style.itemActive, item.active )
 | 
			
		||||
                    , ( "font-semibold", item.selected )
 | 
			
		||||
                    ]
 | 
			
		||||
                , onClick (AddItem item)
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                ]
 | 
			
		||||
                [ text item.option.text
 | 
			
		||||
                , span [ class "text-gray-400 float-right" ]
 | 
			
		||||
                    [ text item.option.additional
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        renderSelectMultiple : Item a -> Html (Msg a)
 | 
			
		||||
        renderSelectMultiple item =
 | 
			
		||||
            a
 | 
			
		||||
                [ class (model.labelColor item.value settings)
 | 
			
		||||
                , class "label font-medium inline-flex relative items-center hover:shadow-md mt-1"
 | 
			
		||||
                , onClick (RemoveItem item)
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ span [ class "pl-4" ]
 | 
			
		||||
                    [ text item.option.text
 | 
			
		||||
                    ]
 | 
			
		||||
                , span [ class "opacity-75 absolute left-2 my-auto" ]
 | 
			
		||||
                    [ i [ class "fa fa-times" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "relative"
 | 
			
		||||
        , onKeyUp KeyPress
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class style.link
 | 
			
		||||
            , class "flex inline-flex flex-wrap items-center"
 | 
			
		||||
            ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "flex flex-row flex-wrap space-x-1 items-center mr-2 -mt-1"
 | 
			
		||||
                , classList [ ( "hidden", List.isEmpty model.selected ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                (List.map renderSelectMultiple model.selected)
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "Search…"
 | 
			
		||||
                , onInput Filter
 | 
			
		||||
                , value model.filterString
 | 
			
		||||
                , class "inline-flex w-16 border-0 px-0 focus:ring-0 h-6"
 | 
			
		||||
                , class style.input
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , a
 | 
			
		||||
                [ class "block h-6 flex-grow"
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                , onClick ToggleMenu
 | 
			
		||||
                , href "#"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i
 | 
			
		||||
                    [ class "fa fa-angle-down px-2"
 | 
			
		||||
                    , class "absolute right-2 rounded cursor-pointer ml-2 top-1/3"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class style.menu
 | 
			
		||||
            , classList [ ( "hidden", not model.menuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            (getOptions model |> List.map renderItem)
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,16 @@ module Comp.Dropzone exposing
 | 
			
		||||
    , setActive
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import File exposing (File)
 | 
			
		||||
import File.Select
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (..)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html exposing (onDragEnter, onDragLeave, onDragOver, onDropFiles)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -28,27 +31,25 @@ type alias State =
 | 
			
		||||
 | 
			
		||||
type alias Settings =
 | 
			
		||||
    { classList : State -> List ( String, Bool )
 | 
			
		||||
    , contentTypes : List String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
defaultSettings : Settings
 | 
			
		||||
defaultSettings =
 | 
			
		||||
    { classList = \_ -> [ ( "ui placeholder segment", True ) ]
 | 
			
		||||
    , contentTypes = []
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
    { state : State
 | 
			
		||||
    , settings : Settings
 | 
			
		||||
    , contentTypes : List String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
init : Settings -> Model
 | 
			
		||||
init settings =
 | 
			
		||||
init : List String -> Model
 | 
			
		||||
init contentTypes =
 | 
			
		||||
    { state = State False True
 | 
			
		||||
    , settings = settings
 | 
			
		||||
    , contentTypes = contentTypes
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -76,7 +77,7 @@ update msg model =
 | 
			
		||||
            ( { model | state = ns }, Cmd.none, [] )
 | 
			
		||||
 | 
			
		||||
        PickFiles ->
 | 
			
		||||
            ( model, File.Select.files model.settings.contentTypes GotFiles, [] )
 | 
			
		||||
            ( model, File.Select.files model.contentTypes GotFiles, [] )
 | 
			
		||||
 | 
			
		||||
        DragEnter ->
 | 
			
		||||
            let
 | 
			
		||||
@@ -99,7 +100,7 @@ update msg model =
 | 
			
		||||
 | 
			
		||||
                newFiles =
 | 
			
		||||
                    if model.state.active then
 | 
			
		||||
                        filterMime model.settings (file :: files)
 | 
			
		||||
                        filterMime model (file :: files)
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        []
 | 
			
		||||
@@ -107,10 +108,10 @@ update msg model =
 | 
			
		||||
            ( { model | state = ns }, Cmd.none, newFiles )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
view : Settings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList (model.settings.classList model.state)
 | 
			
		||||
        [ classList (settings.classList model.state)
 | 
			
		||||
        , onDragEnter DragEnter
 | 
			
		||||
        , onDragOver DragEnter
 | 
			
		||||
        , onDragLeave DragLeave
 | 
			
		||||
@@ -143,14 +144,57 @@ view model =
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
filterMime : Settings -> List File -> List File
 | 
			
		||||
filterMime settings files =
 | 
			
		||||
filterMime : Model -> List File -> List File
 | 
			
		||||
filterMime model files =
 | 
			
		||||
    let
 | 
			
		||||
        pred f =
 | 
			
		||||
            List.member (File.mime f) settings.contentTypes
 | 
			
		||||
            List.member (File.mime f) model.contentTypes
 | 
			
		||||
    in
 | 
			
		||||
    if settings.contentTypes == [] then
 | 
			
		||||
    if model.contentTypes == [] then
 | 
			
		||||
        files
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.filter pred files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "bg-opacity-100 bg-blue-100 dark:bg-lightblue-800", model.state.hover )
 | 
			
		||||
            , ( "bg-blue-100 dark:bg-lightblue-900 bg-opacity-50", not model.state.hover )
 | 
			
		||||
            , ( "disabled", not model.state.active )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex flex-col justify-center items-center py-2 md:py-12 border-0 border-t-2 border-blue-500 dark:border-lightblue-500 dropzone"
 | 
			
		||||
        , onDragEnter DragEnter
 | 
			
		||||
        , onDragOver DragEnter
 | 
			
		||||
        , onDragLeave DragLeave
 | 
			
		||||
        , onDropFiles GotFiles
 | 
			
		||||
        ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class S.header1
 | 
			
		||||
            , class "hidden md:inline-flex items-center"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-mouse-pointer" ] []
 | 
			
		||||
            , div [ class "ml-3" ]
 | 
			
		||||
                [ text "Drop files here"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , B.horizontalDivider
 | 
			
		||||
            { label = "Or"
 | 
			
		||||
            , topCss = "w-2/3 mb-4 hidden md:inline-flex"
 | 
			
		||||
            , labelCss = "px-4 bg-gray-200 bg-opacity-50"
 | 
			
		||||
            , lineColor = "bg-gray-300 dark:bg-bluegray-600"
 | 
			
		||||
            }
 | 
			
		||||
        , B.primaryBasicButton
 | 
			
		||||
            { label = "Select ..."
 | 
			
		||||
            , icon = "fa fa-folder-open font-thin"
 | 
			
		||||
            , handler = onClick PickFiles
 | 
			
		||||
            , attrs = [ href "#" ]
 | 
			
		||||
            , disabled = not model.state.active
 | 
			
		||||
            }
 | 
			
		||||
        , div [ class "text-center opacity-75 text-sm mt-4" ]
 | 
			
		||||
            [ text "Choose document files (pdf, docx, txt, html, …). "
 | 
			
		||||
            , text "Archives (zip and eml) are extracted."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,20 @@ module Comp.EmailInput exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.ContactList exposing (ContactList)
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Data.ContactType
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html exposing (onKeyUp)
 | 
			
		||||
import Util.List
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
@@ -131,6 +135,10 @@ update flags current msg model =
 | 
			
		||||
            ( model, Cmd.none, List.filter (\e -> e /= str) current )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : List String -> Model -> Html Msg
 | 
			
		||||
view values model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -146,9 +154,9 @@ view values model =
 | 
			
		||||
                    , placeholder "Recipients…"
 | 
			
		||||
                    , onKeyUp KeyPress
 | 
			
		||||
                    , onInput SetInput
 | 
			
		||||
                    , value model.input
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text model.input
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
               , renderMenu model
 | 
			
		||||
               ]
 | 
			
		||||
        )
 | 
			
		||||
@@ -188,3 +196,71 @@ renderMenu model =
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map mkItem model.candidates)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : DS.DropdownStyle -> List String -> Model -> Html Msg
 | 
			
		||||
view2 style values model =
 | 
			
		||||
    div [ class "text-sm flex-row space-x-2 relative" ]
 | 
			
		||||
        [ div [ class style.link ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "flex flex-row space-x-2 mr-2"
 | 
			
		||||
                , classList [ ( "hidden", List.isEmpty values ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                (List.map renderValue2 values)
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , value model.input
 | 
			
		||||
                , placeholder "Recipients…"
 | 
			
		||||
                , onKeyUp KeyPress
 | 
			
		||||
                , onInput SetInput
 | 
			
		||||
                , class "inline-flex w-24 border-0 px-0 focus:ring-0 h-6 text-sm"
 | 
			
		||||
                , class "placeholder-gray-400 dark:text-bluegray-200 dark:bg-bluegray-800 dark:border-bluegray-500"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , renderMenu2 style model
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderValue2 : String -> Html Msg
 | 
			
		||||
renderValue2 str =
 | 
			
		||||
    a
 | 
			
		||||
        [ class "label border-gray-400"
 | 
			
		||||
        , class S.border
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , onClick (RemoveEmail str)
 | 
			
		||||
        ]
 | 
			
		||||
        [ span [ class "mr-1" ]
 | 
			
		||||
            [ text str
 | 
			
		||||
            ]
 | 
			
		||||
        , i [ class "fa fa-times" ] []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderMenu2 : DS.DropdownStyle -> Model -> Html Msg
 | 
			
		||||
renderMenu2 style model =
 | 
			
		||||
    let
 | 
			
		||||
        mkItem v =
 | 
			
		||||
            a
 | 
			
		||||
                [ class style.item
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "bg-gray-200 dark:bg-bluegray-700 dark:text-bluegray-50", model.active == Just v )
 | 
			
		||||
                    ]
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick (AddEmail v)
 | 
			
		||||
                ]
 | 
			
		||||
                [ text v
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden", not model.menuOpen )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "-left-2"
 | 
			
		||||
        , class style.menu
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map mkItem model.candidates)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,22 @@ module Comp.EmailSettingsForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.EmailSettings exposing (EmailSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.PasswordInput
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.SSLType exposing (SSLType)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -176,6 +181,10 @@ update msg model =
 | 
			
		||||
            ( { model | ignoreCertificates = not model.ignoreCertificates }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -273,3 +282,131 @@ view settings model =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div [ class "grid grid-cols-4 gap-y-4 gap-x-2" ]
 | 
			
		||||
        [ div [ class "col-span-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Connection name, e.g. 'gmail.com'"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.name == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div
 | 
			
		||||
                [ class S.message
 | 
			
		||||
                , class "mt-2"
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "The connection name must not contain whitespace or special characters."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-3" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "SMTP Host"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "SMTP host name, e.g. 'mail.gmail.com'"
 | 
			
		||||
                , value model.host
 | 
			
		||||
                , onInput SetHost
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.host == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map PortMsg
 | 
			
		||||
            (Comp.IntField.viewWithInfo2 ""
 | 
			
		||||
                model.portNum
 | 
			
		||||
                ""
 | 
			
		||||
                model.portField
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "SMTP User"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "SMTP Username, e.g. 'your.name@gmail.com'"
 | 
			
		||||
                , Maybe.withDefault "" model.user |> value
 | 
			
		||||
                , onInput SetUser
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "SMTP Password"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map PassMsg
 | 
			
		||||
                (Comp.PasswordInput.view2
 | 
			
		||||
                    model.password
 | 
			
		||||
                    False
 | 
			
		||||
                    model.passField
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "From Address"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "Sender E-Mail address"
 | 
			
		||||
                , value model.from
 | 
			
		||||
                , onInput SetFrom
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.from == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Reply-To"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "Optional reply-to E-Mail address"
 | 
			
		||||
                , Maybe.withDefault "" model.replyTo |> value
 | 
			
		||||
                , onInput SetReplyTo
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "SSL"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SSLTypeMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.sslType
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2 flex items-center" ]
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleCheckCert
 | 
			
		||||
                    , label = "Ignore certificate check"
 | 
			
		||||
                    , value = model.ignoreCertificates
 | 
			
		||||
                    , id = "smpt-no-cert-check"
 | 
			
		||||
                    }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,17 @@ module Comp.EmailSettingsManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.EmailSettings
 | 
			
		||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.EmailSettingsForm
 | 
			
		||||
import Comp.EmailSettingsTable
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -20,6 +23,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -200,6 +204,10 @@ update flags msg model =
 | 
			
		||||
            ( { model | loading = False }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    case model.viewMode of
 | 
			
		||||
@@ -287,3 +295,99 @@ viewForm settings model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    case model.viewMode of
 | 
			
		||||
        Table ->
 | 
			
		||||
            viewTable2 model
 | 
			
		||||
 | 
			
		||||
        Form ->
 | 
			
		||||
            viewForm2 settings model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNew
 | 
			
		||||
                    , title = "Add new SMTP settings"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Settings"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.EmailSettingsTable.view2 model.tableModel)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete these connection?"
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col md:relative" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if model.formModel.settings.name /= "" then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this settings entry"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg
 | 
			
		||||
            (Comp.EmailSettingsForm.view2 settings model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", model.formError == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.EmailSettingsTable exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.EmailSettings exposing (EmailSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -42,6 +45,10 @@ update msg model =
 | 
			
		||||
            ( { model | selected = Just ems }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    table [ class "ui selectable pointer table" ]
 | 
			
		||||
@@ -76,3 +83,45 @@ renderLine model ems =
 | 
			
		||||
        , td [] [ text hostport ]
 | 
			
		||||
        , td [] [ text ems.from ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "" ] []
 | 
			
		||||
                , th [ class "text-left mr-2" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left mr-2" ] [ text "Host/Port" ]
 | 
			
		||||
                , th [ class "text-left mr-2 hidden sm:table-cell" ] [ text "From" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderLine2 model) model.emailSettings)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderLine2 : Model -> EmailSettings -> Html Msg
 | 
			
		||||
renderLine2 _ ems =
 | 
			
		||||
    let
 | 
			
		||||
        hostport =
 | 
			
		||||
            case ems.smtpPort of
 | 
			
		||||
                Just p ->
 | 
			
		||||
                    ems.smtpHost ++ ":" ++ String.fromInt p
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ems.smtpHost
 | 
			
		||||
    in
 | 
			
		||||
    tr
 | 
			
		||||
        [ class S.tableRow ]
 | 
			
		||||
        [ B.editLinkTableCell (Select ems)
 | 
			
		||||
        , td [ class "text-left mr-2" ]
 | 
			
		||||
            [ text ems.name
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2" ] [ text hostport ]
 | 
			
		||||
        , td [ class "text-left" ] [ text ems.from ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,16 @@ module Comp.EquipmentForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Equipment exposing (Equipment)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -72,3 +75,38 @@ view model =
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "mb-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "equipname"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Name"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , name "equipname"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "border-red-600 dark:border-orange-600"
 | 
			
		||||
                      , not (isValid model)
 | 
			
		||||
                      )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,24 @@ module Comp.EquipmentManage exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.Equipment
 | 
			
		||||
import Api.Model.EquipmentList exposing (EquipmentList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.EquipmentForm
 | 
			
		||||
import Comp.EquipmentTable
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -300,3 +304,124 @@ viewForm model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
        viewTable2 model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewForm2 model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewEquipment
 | 
			
		||||
                    , title = "Create a new equipment"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Equipment"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.EquipmentTable.view2 model.tableModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "ui dimmer", True )
 | 
			
		||||
                , ( "active", model.loading )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : Model -> Html Msg
 | 
			
		||||
viewForm2 model =
 | 
			
		||||
    let
 | 
			
		||||
        newEquipment =
 | 
			
		||||
            model.formModel.equipment.id == ""
 | 
			
		||||
 | 
			
		||||
        dimmerSettings2 =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this equipment?"
 | 
			
		||||
    in
 | 
			
		||||
    Html.form
 | 
			
		||||
        [ class "relative flex flex-col"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings2
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , if newEquipment then
 | 
			
		||||
            h1 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new equipment"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h1 [ class S.header2 ]
 | 
			
		||||
                [ text model.formModel.equipment.name
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text model.formModel.equipment.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newEquipment then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this equipment"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg (Comp.EquipmentForm.view2 model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,16 @@ module Comp.EquipmentTable exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Equipment exposing (Equipment)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -78,3 +81,34 @@ renderEquipmentLine model equip =
 | 
			
		||||
            [ text equip.name
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Name" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderEquipmentLine2 model) model.equips)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderEquipmentLine2 : Model -> Equipment -> Html Msg
 | 
			
		||||
renderEquipmentLine2 model equip =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just equip ) ]
 | 
			
		||||
        , class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (Select equip)
 | 
			
		||||
        , td [ class "text-left" ]
 | 
			
		||||
            [ text equip.name
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
module Comp.FieldListSelect exposing (..)
 | 
			
		||||
module Comp.FieldListSelect exposing
 | 
			
		||||
    ( Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.Fields exposing (Field)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
@@ -67,3 +74,31 @@ fieldCheckbox selected field =
 | 
			
		||||
            , label [] [ text (Data.Fields.label field) ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> Model -> Html Msg
 | 
			
		||||
view2 classes selected =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col space-y-4 md:space-y-2"
 | 
			
		||||
        , class classes
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map (fieldCheckbox2 selected) Data.Fields.all)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fieldCheckbox2 : Model -> Field -> Html Msg
 | 
			
		||||
fieldCheckbox2 selected field =
 | 
			
		||||
    let
 | 
			
		||||
        isChecked =
 | 
			
		||||
            List.member field selected
 | 
			
		||||
    in
 | 
			
		||||
    MB.viewItem <|
 | 
			
		||||
        MB.Checkbox
 | 
			
		||||
            { id = "field-toggle-" ++ Data.Fields.toString field
 | 
			
		||||
            , value = isChecked
 | 
			
		||||
            , tagger = \_ -> Toggle field
 | 
			
		||||
            , label = Data.Fields.label field
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,17 @@ module Comp.FixedDropdown exposing
 | 
			
		||||
    , initTuple
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    , viewString
 | 
			
		||||
    , viewStyled
 | 
			
		||||
    , viewStyled2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html exposing (KeyCode(..), onKeyUpCode)
 | 
			
		||||
import Util.List
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +38,7 @@ type alias Model a =
 | 
			
		||||
 | 
			
		||||
type Msg a
 | 
			
		||||
    = SelectItem (Item a)
 | 
			
		||||
    | SelectItem2 (Item a)
 | 
			
		||||
    | ToggleMenu
 | 
			
		||||
    | KeyPress (Maybe KeyCode)
 | 
			
		||||
 | 
			
		||||
@@ -122,24 +127,23 @@ update msg model =
 | 
			
		||||
        SelectItem item ->
 | 
			
		||||
            ( model, Just item.id )
 | 
			
		||||
 | 
			
		||||
        SelectItem2 item ->
 | 
			
		||||
            ( { model | menuOpen = False }, Just item.id )
 | 
			
		||||
 | 
			
		||||
        KeyPress (Just Space) ->
 | 
			
		||||
            update ToggleMenu model
 | 
			
		||||
 | 
			
		||||
        KeyPress (Just Enter) ->
 | 
			
		||||
            if not model.menuOpen then
 | 
			
		||||
                update ToggleMenu model
 | 
			
		||||
            let
 | 
			
		||||
                selected =
 | 
			
		||||
                    Util.List.find (isSelected model) model.options
 | 
			
		||||
            in
 | 
			
		||||
            case selected of
 | 
			
		||||
                Just i ->
 | 
			
		||||
                    ( { model | menuOpen = False }, Just i.id )
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                let
 | 
			
		||||
                    selected =
 | 
			
		||||
                        Util.List.find (isSelected model) model.options
 | 
			
		||||
                in
 | 
			
		||||
                case selected of
 | 
			
		||||
                    Just i ->
 | 
			
		||||
                        ( { model | menuOpen = False }, Just i.id )
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        ( model, Nothing )
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ( model, Nothing )
 | 
			
		||||
 | 
			
		||||
        KeyPress (Just Up) ->
 | 
			
		||||
            movePrevious model
 | 
			
		||||
@@ -223,3 +227,63 @@ renderItems model item =
 | 
			
		||||
        ]
 | 
			
		||||
        [ text item.display
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewStyled2 : DS.DropdownStyle -> Bool -> Maybe (Item a) -> Model a -> Html (Msg a)
 | 
			
		||||
viewStyled2 style error sel model =
 | 
			
		||||
    let
 | 
			
		||||
        renderItem item =
 | 
			
		||||
            a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class style.item
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( style.itemActive, isSelected model item )
 | 
			
		||||
                    , ( "font-semibold", Just item == sel )
 | 
			
		||||
                    ]
 | 
			
		||||
                , onClick (SelectItem2 item)
 | 
			
		||||
                ]
 | 
			
		||||
                [ text item.display
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class ("relative " ++ style.root)
 | 
			
		||||
        , onKeyUpCode KeyPress
 | 
			
		||||
        ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ class style.link
 | 
			
		||||
            , classList [ ( S.inputErrorBorder, error ) ]
 | 
			
		||||
            , tabindex 0
 | 
			
		||||
            , onClick ToggleMenu
 | 
			
		||||
            , href "#"
 | 
			
		||||
            ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "flex-grow"
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "opacity-50", sel == Nothing )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ Maybe.map .display sel
 | 
			
		||||
                    |> Maybe.withDefault "Select…"
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ class "rounded cursor-pointer ml-2 absolute right-2"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-angle-down px-2" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class style.menu
 | 
			
		||||
            , classList [ ( "hidden", not model.menuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map renderItem model.options)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Maybe (Item a) -> Model a -> Html (Msg a)
 | 
			
		||||
view2 =
 | 
			
		||||
    viewStyled2 DS.mainStyle False
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.FolderDetail exposing
 | 
			
		||||
    , initEmpty
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -14,14 +15,16 @@ import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.IdResult exposing (IdResult)
 | 
			
		||||
import Api.Model.NewFolder exposing (NewFolder)
 | 
			
		||||
import Api.Model.User exposing (User)
 | 
			
		||||
import Api.Model.UserList exposing (UserList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -416,3 +419,178 @@ viewMember member =
 | 
			
		||||
            [ text member.name
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> Model -> Html Msg
 | 
			
		||||
view2 flags model =
 | 
			
		||||
    let
 | 
			
		||||
        isOwner =
 | 
			
		||||
            Maybe.map .user flags.account
 | 
			
		||||
                |> Maybe.map ((==) model.folder.owner.name)
 | 
			
		||||
                |> Maybe.withDefault False
 | 
			
		||||
 | 
			
		||||
        dimmerSettings : Comp.YesNoDimmer.Settings
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this folder?"
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col md:relative" ]
 | 
			
		||||
        (viewButtons2 model
 | 
			
		||||
            :: [ Html.map DeleteMsg
 | 
			
		||||
                    (Comp.YesNoDimmer.viewN
 | 
			
		||||
                        True
 | 
			
		||||
                        dimmerSettings
 | 
			
		||||
                        model.deleteDimmer
 | 
			
		||||
                    )
 | 
			
		||||
               , div
 | 
			
		||||
                    [ class "py-2 text-lg opacity-75"
 | 
			
		||||
                    , classList [ ( "hidden", model.folder.id /= "" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text "You are automatically set as owner of this new folder."
 | 
			
		||||
                    ]
 | 
			
		||||
               , div
 | 
			
		||||
                    [ class "py-2 text-lg opacity-75"
 | 
			
		||||
                    , classList [ ( "hidden", model.folder.id == "" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text "Modify this folder by changing the name or add/remove members."
 | 
			
		||||
                    ]
 | 
			
		||||
               , div
 | 
			
		||||
                    [ class S.message
 | 
			
		||||
                    , classList [ ( "hidden", model.folder.id == "" || isOwner ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text "You are not the owner of this folder and therefore are not allowed to edit it."
 | 
			
		||||
                    ]
 | 
			
		||||
               , div [ class "mb-4 flex flex-col" ]
 | 
			
		||||
                    [ label
 | 
			
		||||
                        [ class S.inputLabel
 | 
			
		||||
                        , for "folder-name"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ text "Name"
 | 
			
		||||
                        , B.inputRequired
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "flex flex-row space-x-2" ]
 | 
			
		||||
                        [ input
 | 
			
		||||
                            [ type_ "text"
 | 
			
		||||
                            , onInput SetName
 | 
			
		||||
                            , Maybe.withDefault "" model.name
 | 
			
		||||
                                |> value
 | 
			
		||||
                            , class S.textInput
 | 
			
		||||
                            , id "folder-name"
 | 
			
		||||
                            ]
 | 
			
		||||
                            []
 | 
			
		||||
                        , a
 | 
			
		||||
                            [ class S.primaryButton
 | 
			
		||||
                            , class "rounded-r -ml-1"
 | 
			
		||||
                            , onClick SaveName
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-save" ] []
 | 
			
		||||
                            , span [ class "ml-2 hidden sm:inline" ]
 | 
			
		||||
                                [ text "Save"
 | 
			
		||||
                                ]
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
               , div
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( "hidden", model.result == Nothing )
 | 
			
		||||
                        , ( S.errorMessage, Maybe.map .success model.result == Just False )
 | 
			
		||||
                        , ( S.successMessage, Maybe.map .success model.result == Just True )
 | 
			
		||||
                        ]
 | 
			
		||||
                    , class "my-4"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ Maybe.map .message model.result
 | 
			
		||||
                        |> Maybe.withDefault ""
 | 
			
		||||
                        |> text
 | 
			
		||||
                    ]
 | 
			
		||||
               ]
 | 
			
		||||
            ++ viewMembers2 model
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewMembers2 : Model -> List (Html Msg)
 | 
			
		||||
viewMembers2 model =
 | 
			
		||||
    if model.folder.id == "" then
 | 
			
		||||
        []
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class S.header3
 | 
			
		||||
            , class "mt-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Members"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-col space-y-2" ]
 | 
			
		||||
            [ div [ class "flex flex-row space-x-2" ]
 | 
			
		||||
                [ div [ class "flex-grow" ]
 | 
			
		||||
                    [ Html.map MemberDropdownMsg
 | 
			
		||||
                        (Comp.FixedDropdown.view2
 | 
			
		||||
                            (Maybe.map makeItem model.selectedMember)
 | 
			
		||||
                            model.memberDropdown
 | 
			
		||||
                        )
 | 
			
		||||
                    ]
 | 
			
		||||
                , a
 | 
			
		||||
                    [ title "Add a new member"
 | 
			
		||||
                    , onClick AddMember
 | 
			
		||||
                    , class S.primaryButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , class "flex-none"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-plus" ] []
 | 
			
		||||
                    , span [ class "ml-2 hidden sm:inline" ]
 | 
			
		||||
                        [ text "Add"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "flex flex-col space-y-4 md:space-y-2 mt-2"
 | 
			
		||||
            , class "px-2 border-0 border-l dark:border-bluegray-600"
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map viewMember2 model.members)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewMember2 : IdName -> Html Msg
 | 
			
		||||
viewMember2 member =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-row space-x-2 items-center"
 | 
			
		||||
        ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ class S.deleteLabel
 | 
			
		||||
            , href "#"
 | 
			
		||||
            , title "Remove this member"
 | 
			
		||||
            , onClick (RemoveMember member)
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-trash " ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , span [ class "ml-2" ]
 | 
			
		||||
            [ text member.name
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewButtons2 : Model -> Html Msg
 | 
			
		||||
viewButtons2 model =
 | 
			
		||||
    MB.view
 | 
			
		||||
        { start =
 | 
			
		||||
            [ MB.SecondaryButton
 | 
			
		||||
                { tagger = GoBack
 | 
			
		||||
                , label = "Back"
 | 
			
		||||
                , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                , title = "Back to list"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , end =
 | 
			
		||||
            [ MB.DeleteButton
 | 
			
		||||
                { tagger = RequestDelete
 | 
			
		||||
                , label = "Delete"
 | 
			
		||||
                , icon = Just "fa fa-trash"
 | 
			
		||||
                , title = "Delete this folder"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , rootClasses = "mb-4"
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Comp.FolderManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -15,11 +16,13 @@ import Api.Model.User exposing (User)
 | 
			
		||||
import Api.Model.UserList exposing (UserList)
 | 
			
		||||
import Comp.FolderDetail
 | 
			
		||||
import Comp.FolderTable
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -235,3 +238,77 @@ viewTable model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> Model -> Html Msg
 | 
			
		||||
view2 flags model =
 | 
			
		||||
    case model.detailModel of
 | 
			
		||||
        Just dm ->
 | 
			
		||||
            viewDetail2 flags dm
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            viewTable2 model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewDetail2 : Flags -> Comp.FolderDetail.Model -> Html Msg
 | 
			
		||||
viewDetail2 flags model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ if model.folder.id == "" then
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new Folder"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text model.folder.name
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text model.folder.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
        , Html.map DetailMsg (Comp.FolderDetail.view2 flags model)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleOwningOnly
 | 
			
		||||
                    , label = "Show owning folders only"
 | 
			
		||||
                    , value = model.owningOnly
 | 
			
		||||
                    , id = "folder-toggle-owner"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewFolder
 | 
			
		||||
                    , title = "Create a new folder"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Folder"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.FolderTable.view2 model.tableModel model.folders)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "ui dimmer", True )
 | 
			
		||||
                , ( "active", model.loading )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ module Comp.FolderSelect exposing
 | 
			
		||||
    , updateDrop
 | 
			
		||||
    , view
 | 
			
		||||
    , viewDrop
 | 
			
		||||
    , viewDrop2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.FolderStats exposing (FolderStats)
 | 
			
		||||
@@ -231,3 +232,93 @@ viewItem dropModel model item =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewDrop2 : DD.Model -> Int -> Model -> Html Msg
 | 
			
		||||
viewDrop2 dropModel constr model =
 | 
			
		||||
    let
 | 
			
		||||
        highlightDrop =
 | 
			
		||||
            DD.getDropId dropModel == Just DD.FolderRemove
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "ui list" ]
 | 
			
		||||
        [ div [ class "item" ]
 | 
			
		||||
            [ i [ class "folder open icon" ] []
 | 
			
		||||
            , div [ class "content" ]
 | 
			
		||||
                [ div
 | 
			
		||||
                    (classList
 | 
			
		||||
                        [ ( "hidden", True )
 | 
			
		||||
                        , ( "current-drop-target", highlightDrop )
 | 
			
		||||
                        ]
 | 
			
		||||
                        :: DD.droppable FolderDDMsg DD.FolderRemove
 | 
			
		||||
                     -- note: re-enable this when adding a "no-folder selection"
 | 
			
		||||
                     -- this enables a drop target that removes a folder
 | 
			
		||||
                    )
 | 
			
		||||
                    [ text "Folders"
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "flex flex-col space-y-2 md:space-y-1" ]
 | 
			
		||||
                    (renderItems2 dropModel constr model)
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderItems2 : DD.Model -> Int -> Model -> List (Html Msg)
 | 
			
		||||
renderItems2 dropModel constr model =
 | 
			
		||||
    if constr <= 0 then
 | 
			
		||||
        List.map (viewItem2 dropModel model) model.all
 | 
			
		||||
 | 
			
		||||
    else if model.expanded then
 | 
			
		||||
        List.map (viewItem2 dropModel model) model.all ++ collapseToggle constr model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.map (viewItem2 dropModel model) (List.take constr model.all) ++ expandToggle constr model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : DD.Model -> Model -> FolderStats -> Html Msg
 | 
			
		||||
viewItem2 dropModel model item =
 | 
			
		||||
    let
 | 
			
		||||
        selected =
 | 
			
		||||
            Just item.id == model.selected
 | 
			
		||||
 | 
			
		||||
        icon =
 | 
			
		||||
            if selected then
 | 
			
		||||
                "fa fa-folder-open font-thin"
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                "fa fa-folder font-thin"
 | 
			
		||||
 | 
			
		||||
        highlightDrop =
 | 
			
		||||
            DD.getDropId dropModel == Just (DD.Folder item.id)
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        ([ classList
 | 
			
		||||
            [ ( "current-drop-target", highlightDrop )
 | 
			
		||||
            ]
 | 
			
		||||
         , class "flex flex-row items-center"
 | 
			
		||||
         , class "rounded px-1 py-1 hover:bg-blue-100 dark:hover:bg-bluegray-600"
 | 
			
		||||
         , href "#"
 | 
			
		||||
         , onClick (Toggle item)
 | 
			
		||||
         ]
 | 
			
		||||
            ++ DD.droppable FolderDDMsg (DD.Folder item.id)
 | 
			
		||||
        )
 | 
			
		||||
        [ i [ class icon ] []
 | 
			
		||||
        , div [ class "ml-2" ]
 | 
			
		||||
            [ text item.name
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , numberLabel item.count
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
numberLabel : Int -> Html msg
 | 
			
		||||
numberLabel num =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "bg-gray-200 border rounded-full h-6 w-6 flex items-center justify-center text-xs"
 | 
			
		||||
        , class "dark:bg-bluegray-800 dark:text-bluegray-200 dark:border-bluegray-800 dark:bg-opacity-50"
 | 
			
		||||
        ]
 | 
			
		||||
        [ text (String.fromInt num)
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.FolderTable exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.FolderItem exposing (FolderItem)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
@@ -40,6 +43,10 @@ update msg model =
 | 
			
		||||
            ( model, EditAction item )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List FolderItem -> Html Msg
 | 
			
		||||
view _ items =
 | 
			
		||||
    div []
 | 
			
		||||
@@ -91,3 +98,61 @@ viewItem item =
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> List FolderItem -> Html Msg
 | 
			
		||||
view2 _ items =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "w-px whitespace-nowrap pr-1 md:pr-3" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left hidden sm:table-cell" ] [ text "Owner" ]
 | 
			
		||||
                , th [ class "text-center" ]
 | 
			
		||||
                    [ span [ class "hidden sm:inline" ]
 | 
			
		||||
                        [ text "#Member"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , span [ class "sm:hidden" ]
 | 
			
		||||
                        [ text "#"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , th [ class "text-center" ] [ text "Created" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map viewItem2 items)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : FolderItem -> Html Msg
 | 
			
		||||
viewItem2 item =
 | 
			
		||||
    tr
 | 
			
		||||
        [ class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (EditItem item)
 | 
			
		||||
        , td [ class " py-4 md:py-2" ]
 | 
			
		||||
            [ text item.name
 | 
			
		||||
            , span
 | 
			
		||||
                [ classList [ ( "hidden", item.isMember ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ span [ class "ml-1 text-red-700" ]
 | 
			
		||||
                    [ text "*"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class " py-4 md:py-2 hidden sm:table-cell" ]
 | 
			
		||||
            [ text item.owner.name
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center  py-4 md:py-2" ]
 | 
			
		||||
            [ String.fromInt item.memberCount
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center  py-4 md:py-2" ]
 | 
			
		||||
            [ Util.Time.formatDateShort item.created
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,17 +7,22 @@ module Comp.ImapSettingsForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.ImapSettings exposing (ImapSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.PasswordInput
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.SSLType exposing (SSLType)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -168,6 +173,10 @@ update msg model =
 | 
			
		||||
            ( { model | useOAuthToken = not model.useOAuthToken }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -254,3 +263,111 @@ view settings model =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "grid grid-cols-4 gap-y-4 gap-x-2" ]
 | 
			
		||||
        [ div [ class "col-span-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Connection name, e.g. 'gmail.com'"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.name == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div
 | 
			
		||||
                [ class S.message
 | 
			
		||||
                , class "mt-2"
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "The connection name must not contain whitespace or special characters."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-3" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "IMAP Host"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "IMAP host name, e.g. 'mail.gmail.com'"
 | 
			
		||||
                , value model.host
 | 
			
		||||
                , onInput SetHost
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.host == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map PortMsg
 | 
			
		||||
            (Comp.IntField.viewWithInfo2 ""
 | 
			
		||||
                model.portNum
 | 
			
		||||
                ""
 | 
			
		||||
                model.portField
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "IMAP User"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , placeholder "IMAP Username, e.g. 'your.name@gmail.com'"
 | 
			
		||||
                , Maybe.withDefault "" model.user |> value
 | 
			
		||||
                , onInput SetUser
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "IMAP Password" ]
 | 
			
		||||
            , Html.map PassMsg
 | 
			
		||||
                (Comp.PasswordInput.view2
 | 
			
		||||
                    model.password
 | 
			
		||||
                    False
 | 
			
		||||
                    model.passField
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "SSL"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SSLTypeMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.sslType
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2 flex items-center" ]
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleCheckCert
 | 
			
		||||
                    , label = "Ignore certificate check"
 | 
			
		||||
                    , value = model.ignoreCertificates
 | 
			
		||||
                    , id = "imap-no-cert-check"
 | 
			
		||||
                    }
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "col-span-4 sm:col-span-2 flex flex-col" ]
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleUseOAuth
 | 
			
		||||
                    , label = "Enable OAuth2 authentication"
 | 
			
		||||
                    , value = model.useOAuthToken
 | 
			
		||||
                    , id = "imap-use-oauth"
 | 
			
		||||
                    }
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Enabling this, allows to connect via XOAuth using the password as access token."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,17 @@ module Comp.ImapSettingsManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.ImapSettings
 | 
			
		||||
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.ImapSettingsForm
 | 
			
		||||
import Comp.ImapSettingsTable
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -20,6 +23,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -200,6 +204,10 @@ update flags msg model =
 | 
			
		||||
            ( { model | loading = False }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    case model.viewMode of
 | 
			
		||||
@@ -287,3 +295,105 @@ viewForm settings model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    case model.viewMode of
 | 
			
		||||
        Table ->
 | 
			
		||||
            viewTable2 model
 | 
			
		||||
 | 
			
		||||
        Form ->
 | 
			
		||||
            viewForm2 settings model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNew
 | 
			
		||||
                    , title = "Add new SMTP settings"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Settings"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg
 | 
			
		||||
            (Comp.ImapSettingsTable.view2
 | 
			
		||||
                model.tableModel
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this mail-box connection?"
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col md:relative" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if model.formModel.settings.name /= "" then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this settings entry"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg
 | 
			
		||||
            (Comp.ImapSettingsForm.view2
 | 
			
		||||
                settings
 | 
			
		||||
                model.formModel
 | 
			
		||||
            )
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", model.formError == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.ImapSettingsTable exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.ImapSettings exposing (ImapSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -42,6 +45,10 @@ update msg model =
 | 
			
		||||
            ( { model | selected = Just ems }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    table [ class "ui selectable pointer table" ]
 | 
			
		||||
@@ -74,3 +81,41 @@ renderLine model ems =
 | 
			
		||||
        [ td [ class "collapsible" ] [ text ems.name ]
 | 
			
		||||
        , td [] [ text hostport ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [] []
 | 
			
		||||
                , th [ class "text-left mr-2" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left mr-2" ] [ text "Host/Port" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderLine2 model) model.emailSettings)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderLine2 : Model -> ImapSettings -> Html Msg
 | 
			
		||||
renderLine2 _ ems =
 | 
			
		||||
    let
 | 
			
		||||
        hostport =
 | 
			
		||||
            case ems.imapPort of
 | 
			
		||||
                Just p ->
 | 
			
		||||
                    ems.imapHost ++ ":" ++ String.fromInt p
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ems.imapHost
 | 
			
		||||
    in
 | 
			
		||||
    tr
 | 
			
		||||
        [ class S.tableRow ]
 | 
			
		||||
        [ B.editLinkTableCell (Select ems)
 | 
			
		||||
        , td [ class "text-left mr-2" ] [ text ems.name ]
 | 
			
		||||
        , td [ class "text-left" ] [ text hostport ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,14 @@ module Comp.IntField exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , viewWithInfo
 | 
			
		||||
    , viewWithInfo2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Markdown
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -133,3 +135,46 @@ viewWithInfo info nval classes model =
 | 
			
		||||
            [ Maybe.withDefault "" model.error |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewWithInfo2 : String -> Maybe Int -> String -> Model -> Html Msg
 | 
			
		||||
viewWithInfo2 info nval classes model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( classes, True )
 | 
			
		||||
            , ( "error", model.error /= Nothing )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text model.label
 | 
			
		||||
            ]
 | 
			
		||||
        , input
 | 
			
		||||
            [ type_ "text"
 | 
			
		||||
            , Maybe.map String.fromInt nval
 | 
			
		||||
                |> Maybe.withDefault model.lastInput
 | 
			
		||||
                |> value
 | 
			
		||||
            , onInput SetValue
 | 
			
		||||
            , class S.textInput
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , span
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", info == "" )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "opacity-50 text-sm"
 | 
			
		||||
            ]
 | 
			
		||||
            [ Markdown.toHtml [] info
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", model.error == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.error |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ module Comp.ItemCard exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -25,6 +26,7 @@ import Html.Events exposing (onClick)
 | 
			
		||||
import Markdown
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.CustomField
 | 
			
		||||
import Util.ItemDragDrop as DD
 | 
			
		||||
import Util.List
 | 
			
		||||
@@ -91,6 +93,10 @@ currentPosition model item =
 | 
			
		||||
            1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Update
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : DD.Model -> Msg -> Model -> UpdateResult
 | 
			
		||||
update ddm msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
@@ -129,6 +135,10 @@ update ddm msg model =
 | 
			
		||||
            UpdateResult model ddm Data.ItemSelection.Inactive target
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : ViewConfig -> UiSettings -> Model -> ItemLight -> Html Msg
 | 
			
		||||
view cfg settings model item =
 | 
			
		||||
    let
 | 
			
		||||
@@ -236,7 +246,7 @@ metaDataContent settings item =
 | 
			
		||||
                , title "Correspondent"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.correspondentIcon ""
 | 
			
		||||
                    :: Comp.LinkTarget.makeCorrLink item SetLinkTarget
 | 
			
		||||
                    :: Comp.LinkTarget.makeCorrLink item [] SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , div
 | 
			
		||||
                [ classList
 | 
			
		||||
@@ -249,7 +259,7 @@ metaDataContent settings item =
 | 
			
		||||
                , title "Concerning"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.concernedIcon
 | 
			
		||||
                    :: Comp.LinkTarget.makeConcLink item SetLinkTarget
 | 
			
		||||
                    :: Comp.LinkTarget.makeConcLink item [] SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , div
 | 
			
		||||
                [ classList
 | 
			
		||||
@@ -259,7 +269,7 @@ metaDataContent settings item =
 | 
			
		||||
                , title "Folder"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.folderIcon ""
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink item SetLinkTarget
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink item [] SetLinkTarget
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "right floated meta" ]
 | 
			
		||||
@@ -545,6 +555,435 @@ renderHighlightEntry entry =
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : ViewConfig -> UiSettings -> Model -> ItemLight -> Html Msg
 | 
			
		||||
view2 cfg settings model item =
 | 
			
		||||
    let
 | 
			
		||||
        isConfirmed =
 | 
			
		||||
            item.state /= "created"
 | 
			
		||||
 | 
			
		||||
        cardColor =
 | 
			
		||||
            if not isConfirmed then
 | 
			
		||||
                "text-blue-500 dark:text-lightblue-500"
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                ""
 | 
			
		||||
 | 
			
		||||
        fieldHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        cardAction =
 | 
			
		||||
            case cfg.selection of
 | 
			
		||||
                Data.ItemSelection.Inactive ->
 | 
			
		||||
                    [ Page.href (ItemDetailPage item.id)
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                Data.ItemSelection.Active ids ->
 | 
			
		||||
                    [ onClick (ToggleSelectItem ids item.id)
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
        selectedDimmer =
 | 
			
		||||
            div
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "hidden", not (isSelected cfg item.id) )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class S.dimmerCard
 | 
			
		||||
                , class "rounded-lg"
 | 
			
		||||
                ]
 | 
			
		||||
                [ div [ class "text-9xl text-blue-400 hover:text-blue-500 dark:text-lightblue-300 dark:hover:text-lightblue-200" ]
 | 
			
		||||
                    [ a
 | 
			
		||||
                        cardAction
 | 
			
		||||
                        [ i [ class "fa fa-check-circle font-thin" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        ([ class cfg.extraClasses
 | 
			
		||||
         , class "ds-item-card relative hover:shadow-lg rounded-lg flex flex-col"
 | 
			
		||||
         , classList
 | 
			
		||||
            [ ( "border border-gray-400 dark:border-bluegray-600 dark:hover:border-bluegray-500", not (isMultiSelectMode cfg) )
 | 
			
		||||
            , ( "border-2 border-gray-800 border-dashed dark:border-lightblue-500", isMultiSelectMode cfg )
 | 
			
		||||
            ]
 | 
			
		||||
         , id item.id
 | 
			
		||||
         ]
 | 
			
		||||
            ++ DD.draggable ItemDDMsg item.id
 | 
			
		||||
        )
 | 
			
		||||
        ((if fieldHidden Data.Fields.PreviewImage then
 | 
			
		||||
            []
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            [ previewImage2 settings cardAction model item
 | 
			
		||||
            ]
 | 
			
		||||
         )
 | 
			
		||||
            ++ [ mainContent2 cardAction cardColor isConfirmed settings cfg item
 | 
			
		||||
               , metaDataContent2 settings item
 | 
			
		||||
               , notesContent2 settings item
 | 
			
		||||
               , fulltextResultsContent2 item
 | 
			
		||||
               , previewMenu2 settings model item (currentAttachment model item)
 | 
			
		||||
               , selectedDimmer
 | 
			
		||||
               ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fulltextResultsContent2 : ItemLight -> Html Msg
 | 
			
		||||
fulltextResultsContent2 item =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "ds-card-search-hl flex flex-col text-sm px-2 bg-yellow-50 dark:bg-indigo-800 dark:bg-opacity-60 "
 | 
			
		||||
        , classList
 | 
			
		||||
            [ ( "hidden", item.highlighting == [] )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map renderHighlightEntry2 item.highlighting)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
metaDataContent2 : UiSettings -> ItemLight -> Html Msg
 | 
			
		||||
metaDataContent2 settings item =
 | 
			
		||||
    let
 | 
			
		||||
        fieldHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "px-2 pb-1 flex flex-row items-center justify-between text-sm opacity-80" ]
 | 
			
		||||
        [ div [ class "flex flex-row justify-between" ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "hidden", fieldHidden Data.Fields.Folder )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class "hover:opacity-60"
 | 
			
		||||
                , title "Folder"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.folderIcon2 "mr-2"
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink item
 | 
			
		||||
                    [ ( "hover:opacity-60", True ) ]
 | 
			
		||||
                    SetLinkTarget
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , div [ class "flex flex-row items-center justify-end" ]
 | 
			
		||||
            [ div [ class "" ]
 | 
			
		||||
                [ Icons.sourceIcon2 "mr-2"
 | 
			
		||||
                , Comp.LinkTarget.makeSourceLink
 | 
			
		||||
                    [ ( "hover:opacity-60", True ) ]
 | 
			
		||||
                    SetLinkTarget
 | 
			
		||||
                    (IT.render IT.source item)
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
notesContent2 : UiSettings -> ItemLight -> Html Msg
 | 
			
		||||
notesContent2 settings item =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden"
 | 
			
		||||
              , settings.itemSearchNoteLength
 | 
			
		||||
                    <= 0
 | 
			
		||||
                    || Util.String.isNothingOrBlank item.notes
 | 
			
		||||
              )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "px-2 py-2 border-t  dark:border-bluegray-600 opacity-50 text-sm"
 | 
			
		||||
        ]
 | 
			
		||||
        [ Maybe.withDefault "" item.notes
 | 
			
		||||
            |> Util.String.ellipsis settings.itemSearchNoteLength
 | 
			
		||||
            |> text
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mainContent2 : List (Attribute Msg) -> String -> Bool -> UiSettings -> ViewConfig -> ItemLight -> Html Msg
 | 
			
		||||
mainContent2 cardAction cardColor isConfirmed settings _ item =
 | 
			
		||||
    let
 | 
			
		||||
        dirIcon =
 | 
			
		||||
            i
 | 
			
		||||
                [ class (Data.Direction.iconFromMaybe2 item.direction)
 | 
			
		||||
                , class "mr-2 w-4 text-center"
 | 
			
		||||
                , classList [ ( "hidden", fieldHidden Data.Fields.Direction ) ]
 | 
			
		||||
                , IT.render IT.direction item |> title
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
 | 
			
		||||
        fieldHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        titlePattern =
 | 
			
		||||
            settings.cardTitleTemplate.template
 | 
			
		||||
 | 
			
		||||
        subtitlePattern =
 | 
			
		||||
            settings.cardSubtitleTemplate.template
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col px-2 py-2 mb-auto" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden"
 | 
			
		||||
                  , fieldHidden Data.Fields.CorrOrg
 | 
			
		||||
                        && fieldHidden Data.Fields.CorrPerson
 | 
			
		||||
                  )
 | 
			
		||||
                ]
 | 
			
		||||
            , title "Correspondent"
 | 
			
		||||
            ]
 | 
			
		||||
            (Icons.correspondentIcon2 "mr-2 w-4 text-center"
 | 
			
		||||
                :: Comp.LinkTarget.makeCorrLink item [ ( "hover:opacity-75", True ) ] SetLinkTarget
 | 
			
		||||
            )
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden"
 | 
			
		||||
                  , fieldHidden Data.Fields.ConcPerson
 | 
			
		||||
                        && fieldHidden Data.Fields.ConcEquip
 | 
			
		||||
                  )
 | 
			
		||||
                ]
 | 
			
		||||
            , title "Concerning"
 | 
			
		||||
            ]
 | 
			
		||||
            (Icons.concernedIcon2 "mr-2 w-4 text-center"
 | 
			
		||||
                :: Comp.LinkTarget.makeConcLink item [ ( "hover:opacity-75", True ) ] SetLinkTarget
 | 
			
		||||
            )
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "font-bold py-1 text-lg"
 | 
			
		||||
            , classList [ ( "hidden", IT.render titlePattern item == "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ IT.render titlePattern item |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "absolute right-1 top-1 text-4xl", True )
 | 
			
		||||
                , ( cardColor, True )
 | 
			
		||||
                , ( "hidden", isConfirmed )
 | 
			
		||||
                ]
 | 
			
		||||
            , title "New"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "ml-2 fa fa-exclamation-circle" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "opacity-75", True )
 | 
			
		||||
                , ( "hidden", IT.render subtitlePattern item == "" )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ dirIcon
 | 
			
		||||
            , IT.render subtitlePattern item |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ mainTagsAndFields2 settings item
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
mainTagsAndFields2 : UiSettings -> ItemLight -> Html Msg
 | 
			
		||||
mainTagsAndFields2 settings item =
 | 
			
		||||
    let
 | 
			
		||||
        fieldHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        hideTags =
 | 
			
		||||
            item.tags == [] || fieldHidden Data.Fields.Tag
 | 
			
		||||
 | 
			
		||||
        hideFields =
 | 
			
		||||
            item.customfields == [] || fieldHidden Data.Fields.CustomFields
 | 
			
		||||
 | 
			
		||||
        showTag tag =
 | 
			
		||||
            Comp.LinkTarget.makeTagIconLink
 | 
			
		||||
                tag
 | 
			
		||||
                (i [ class "fa fa-tag mr-2" ] [])
 | 
			
		||||
                [ ( "label ml-1 mt-1 font-semibold hover:opacity-75", True )
 | 
			
		||||
                , ( Data.UiSettings.tagColorString2 tag settings, True )
 | 
			
		||||
                ]
 | 
			
		||||
                SetLinkTarget
 | 
			
		||||
 | 
			
		||||
        showField fv =
 | 
			
		||||
            Comp.LinkTarget.makeCustomFieldLink2 fv
 | 
			
		||||
                [ ( S.basicLabel, True )
 | 
			
		||||
                , ( "ml-1 mt-1 font-semibold hover:opacity-75", True )
 | 
			
		||||
                ]
 | 
			
		||||
                SetLinkTarget
 | 
			
		||||
 | 
			
		||||
        renderFields =
 | 
			
		||||
            if hideFields then
 | 
			
		||||
                []
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                List.sortBy Util.CustomField.nameOrLabel item.customfields
 | 
			
		||||
                    |> List.map showField
 | 
			
		||||
 | 
			
		||||
        renderTags =
 | 
			
		||||
            if hideTags then
 | 
			
		||||
                []
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                List.map showTag item.tags
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "flex flex-row items-center flex-wrap justify-end text-xs font-medium my-1", True )
 | 
			
		||||
            , ( "hidden", hideTags && hideFields )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        (renderFields ++ renderTags)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
previewImage2 : UiSettings -> List (Attribute Msg) -> Model -> ItemLight -> Html Msg
 | 
			
		||||
previewImage2 settings cardAction model item =
 | 
			
		||||
    let
 | 
			
		||||
        mainAttach =
 | 
			
		||||
            currentAttachment model item
 | 
			
		||||
 | 
			
		||||
        previewUrl =
 | 
			
		||||
            Maybe.map .id mainAttach
 | 
			
		||||
                |> Maybe.map Api.attachmentPreviewURL
 | 
			
		||||
                |> Maybe.withDefault (Api.itemBasePreviewURL item.id)
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        ([ class "overflow-hidden block bg-gray-50 dark:bg-bluegray-700 dark:bg-opacity-40  border-gray-400 dark:hover:border-bluegray-500 rounded-t-lg"
 | 
			
		||||
         , class (Data.UiSettings.cardPreviewSize2 settings)
 | 
			
		||||
         ]
 | 
			
		||||
            ++ cardAction
 | 
			
		||||
        )
 | 
			
		||||
        [ img
 | 
			
		||||
            [ class "preview-image mx-auto pt-1"
 | 
			
		||||
            , classList
 | 
			
		||||
                [ ( "rounded-t-lg w-full -mt-1", settings.cardPreviewFullWidth )
 | 
			
		||||
                , ( Data.UiSettings.cardPreviewSize2 settings, not settings.cardPreviewFullWidth )
 | 
			
		||||
                ]
 | 
			
		||||
            , src previewUrl
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
previewMenu2 : UiSettings -> Model -> ItemLight -> Maybe AttachmentLight -> Html Msg
 | 
			
		||||
previewMenu2 settings model item mainAttach =
 | 
			
		||||
    let
 | 
			
		||||
        pageCount =
 | 
			
		||||
            Maybe.andThen .pageCount mainAttach
 | 
			
		||||
                |> Maybe.withDefault 0
 | 
			
		||||
 | 
			
		||||
        attachCount =
 | 
			
		||||
            List.length item.attachments
 | 
			
		||||
 | 
			
		||||
        fieldHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        mkAttachUrl id =
 | 
			
		||||
            if settings.nativePdfPreview then
 | 
			
		||||
                Api.fileURL id
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                Api.fileURL id ++ "/view"
 | 
			
		||||
 | 
			
		||||
        attachUrl =
 | 
			
		||||
            Maybe.map .id mainAttach
 | 
			
		||||
                |> Maybe.map mkAttachUrl
 | 
			
		||||
                |> Maybe.withDefault "/api/v1/sec/attachment/none"
 | 
			
		||||
 | 
			
		||||
        dueDate =
 | 
			
		||||
            IT.render IT.dueDateShort item
 | 
			
		||||
 | 
			
		||||
        dueDateLabel =
 | 
			
		||||
            div
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( " hidden"
 | 
			
		||||
                      , item.dueDate
 | 
			
		||||
                            == Nothing
 | 
			
		||||
                            || fieldHidden Data.Fields.DueDate
 | 
			
		||||
                      )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class "label font-semibold text-sm border-gray-300 dark:border-bluegray-600"
 | 
			
		||||
                , title ("Due on " ++ dueDate)
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.dueDateIcon2 "mr-2"
 | 
			
		||||
                , text (" " ++ dueDate)
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "px-2 py-1 flex flex-row flex-wrap bg-gray-50 dark:bg-bluegray-700 dark:bg-opacity-40 border-0 rounded-b-lg md:text-sm" ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ class S.secondaryBasicButtonPlain
 | 
			
		||||
            , class "px-2 py-1 border rounded "
 | 
			
		||||
            , href attachUrl
 | 
			
		||||
            , target "_self"
 | 
			
		||||
            , title "Open attachment file"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-eye" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , a
 | 
			
		||||
            [ class S.secondaryBasicButtonPlain
 | 
			
		||||
            , class "px-2 py-1 border rounded ml-2"
 | 
			
		||||
            , Page.href (ItemDetailPage item.id)
 | 
			
		||||
            , title "Go to detail view"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-edit" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList [ ( "hidden", attachCount > 1 && not (fieldHidden Data.Fields.PreviewImage) ) ]
 | 
			
		||||
            , class "ml-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "px-2 rounded border border-gray-300 dark:border-bluegray-600 py-1" ]
 | 
			
		||||
                [ text (String.fromInt pageCount)
 | 
			
		||||
                , text "p."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "flex flex-row items-center ml-2"
 | 
			
		||||
            , classList [ ( "hidden", attachCount <= 1 || fieldHidden Data.Fields.PreviewImage ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ class S.secondaryBasicButtonPlain
 | 
			
		||||
                , class "px-2 py-1 border rounded-l block"
 | 
			
		||||
                , title "Cycle attachments"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick (CyclePreview item)
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-arrow-right" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "px-2 rounded-r border-t border-r border-b border-gray-500 dark:border-bluegray-500 py-1" ]
 | 
			
		||||
                [ currentPosition model item
 | 
			
		||||
                    |> String.fromInt
 | 
			
		||||
                    |> text
 | 
			
		||||
                , text "/"
 | 
			
		||||
                , text (attachCount |> String.fromInt)
 | 
			
		||||
                , text ", "
 | 
			
		||||
                , text (String.fromInt pageCount)
 | 
			
		||||
                , text "p."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , div [ class "flex flex-row items-center justify-end" ]
 | 
			
		||||
            [ dueDateLabel
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderHighlightEntry2 : HighlightEntry -> Html Msg
 | 
			
		||||
renderHighlightEntry2 entry =
 | 
			
		||||
    let
 | 
			
		||||
        stripWhitespace str =
 | 
			
		||||
            String.trim str
 | 
			
		||||
                |> String.replace "```" ""
 | 
			
		||||
                |> String.replace "\t" "  "
 | 
			
		||||
                |> String.replace "\n\n" "\n"
 | 
			
		||||
                |> String.lines
 | 
			
		||||
                |> List.map String.trim
 | 
			
		||||
                |> String.join "\n"
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "content" ]
 | 
			
		||||
        (div [ class "font-semibold" ]
 | 
			
		||||
            [ i [ class "fa fa-caret-right mr-1 " ] []
 | 
			
		||||
            , text (entry.name ++ ":")
 | 
			
		||||
            ]
 | 
			
		||||
            :: List.map
 | 
			
		||||
                (\str ->
 | 
			
		||||
                    Markdown.toHtml [ class "opacity-80 " ] <|
 | 
			
		||||
                        (stripWhitespace str ++ "…")
 | 
			
		||||
                )
 | 
			
		||||
                entry.lines
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isSelected : ViewConfig -> String -> Bool
 | 
			
		||||
isSelected cfg id =
 | 
			
		||||
    case cfg.selection of
 | 
			
		||||
@@ -553,3 +992,13 @@ isSelected cfg id =
 | 
			
		||||
 | 
			
		||||
        Data.ItemSelection.Inactive ->
 | 
			
		||||
            False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isMultiSelectMode : ViewConfig -> Bool
 | 
			
		||||
isMultiSelectMode cfg =
 | 
			
		||||
    case cfg.selection of
 | 
			
		||||
        Data.ItemSelection.Active _ ->
 | 
			
		||||
            True
 | 
			
		||||
 | 
			
		||||
        Data.ItemSelection.Inactive ->
 | 
			
		||||
            False
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ module Comp.ItemCardList exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , updateDrag
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.ItemLight exposing (ItemLight)
 | 
			
		||||
@@ -23,6 +24,7 @@ import Dict exposing (Dict)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.ItemDragDrop as DD
 | 
			
		||||
import Util.List
 | 
			
		||||
 | 
			
		||||
@@ -194,6 +196,74 @@ viewItem model cfg settings item =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : ViewConfig -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 cfg settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "ds-item-list", True )
 | 
			
		||||
            , ( "ds-multi-select-mode", isMultiSelectMode cfg )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map (viewGroup2 model cfg settings) model.results.groups)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewGroup2 : Model -> ViewConfig -> UiSettings -> ItemLightGroup -> Html Msg
 | 
			
		||||
viewGroup2 model cfg settings group =
 | 
			
		||||
    div [ class "ds-item-group" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "flex py-0 mt-2 flex flex-row items-center"
 | 
			
		||||
            , class "bg-white dark:bg-bluegray-800 text-lg z-35"
 | 
			
		||||
            , class "relative sticky top-10"
 | 
			
		||||
            ]
 | 
			
		||||
            [ hr
 | 
			
		||||
                [ class S.border
 | 
			
		||||
                , class "flex-grow"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div [ class "px-6" ]
 | 
			
		||||
                [ i [ class "fa fa-calendar-alt font-thin" ] []
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ text group.name
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , hr
 | 
			
		||||
                [ class S.border
 | 
			
		||||
                , class "flex-grow"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-2" ]
 | 
			
		||||
            (List.map (viewItem2 model cfg settings) group.items)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : Model -> ViewConfig -> UiSettings -> ItemLight -> Html Msg
 | 
			
		||||
viewItem2 model cfg settings item =
 | 
			
		||||
    let
 | 
			
		||||
        currentClass =
 | 
			
		||||
            if cfg.current == Just item.id then
 | 
			
		||||
                "current"
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                ""
 | 
			
		||||
 | 
			
		||||
        vvcfg =
 | 
			
		||||
            Comp.ItemCard.ViewConfig cfg.selection currentClass
 | 
			
		||||
 | 
			
		||||
        cardModel =
 | 
			
		||||
            Dict.get item.id model.itemCards
 | 
			
		||||
                |> Maybe.withDefault Comp.ItemCard.init
 | 
			
		||||
 | 
			
		||||
        cardHtml =
 | 
			
		||||
            Comp.ItemCard.view2 vvcfg settings cardModel item
 | 
			
		||||
    in
 | 
			
		||||
    Html.map (ItemCardMsg item) cardHtml
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,14 @@ module Comp.ItemDetail exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Browser.Navigation as Nav
 | 
			
		||||
import Comp.ItemDetail.Model exposing (Msg(..), UpdateResult)
 | 
			
		||||
import Comp.ItemDetail.Update
 | 
			
		||||
import Comp.ItemDetail.View exposing (..)
 | 
			
		||||
import Comp.ItemDetail.View
 | 
			
		||||
import Comp.ItemDetail.View2
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.ItemNav exposing (ItemNav)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -33,3 +35,8 @@ update =
 | 
			
		||||
view : ItemNav -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view =
 | 
			
		||||
    Comp.ItemDetail.View.view
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : ItemNav -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 =
 | 
			
		||||
    Comp.ItemDetail.View2.view
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/AddFilesForm.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
module Comp.ItemDetail.AddFilesForm exposing (view)
 | 
			
		||||
 | 
			
		||||
import Comp.Dropzone
 | 
			
		||||
import Comp.ItemDetail.Model exposing (..)
 | 
			
		||||
import Comp.Progress
 | 
			
		||||
import Data.DropdownStyle
 | 
			
		||||
import Dict
 | 
			
		||||
import File exposing (File)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Set
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.File exposing (makeFileId)
 | 
			
		||||
import Util.Size
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden", not model.addFilesOpen )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex flex-col px-2 py-2 mb-4"
 | 
			
		||||
        , class S.box
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "text-lg font-bold" ]
 | 
			
		||||
            [ text "Add more files to this item"
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map AddFilesMsg
 | 
			
		||||
            (Comp.Dropzone.view2 model.addFilesModel)
 | 
			
		||||
        , div [ class "flex flex-row space-x-2 mt-2" ]
 | 
			
		||||
            [ button
 | 
			
		||||
                [ class S.primaryButton
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick AddFilesSubmitUpload
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Submit"
 | 
			
		||||
                ]
 | 
			
		||||
            , button
 | 
			
		||||
                [ class S.secondaryButton
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick AddFilesReset
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Reset"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.successMessage, True )
 | 
			
		||||
                , ( "hidden", model.selectedFiles == [] || not (isSuccessAll model) )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "mt-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "All files have been uploaded. They are being processed, some data "
 | 
			
		||||
            , text "may not be available immediately. "
 | 
			
		||||
            , a
 | 
			
		||||
                [ class S.successMessageLink
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick ReloadItem
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Refresh now"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "flex flex-col mt-2"
 | 
			
		||||
            , classList [ ( "hidden", List.isEmpty model.selectedFiles || isSuccessAll model ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map (renderFileItem model) model.selectedFiles)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderFileItem : Model -> File -> Html Msg
 | 
			
		||||
renderFileItem model file =
 | 
			
		||||
    let
 | 
			
		||||
        name =
 | 
			
		||||
            File.name file
 | 
			
		||||
 | 
			
		||||
        size =
 | 
			
		||||
            File.size file
 | 
			
		||||
                |> toFloat
 | 
			
		||||
                |> Util.Size.bytesReadable Util.Size.B
 | 
			
		||||
 | 
			
		||||
        getProgress =
 | 
			
		||||
            let
 | 
			
		||||
                key =
 | 
			
		||||
                    makeFileId file
 | 
			
		||||
            in
 | 
			
		||||
            Dict.get key model.loading
 | 
			
		||||
                |> Maybe.withDefault 0
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "flex flex-row items-center" ]
 | 
			
		||||
            [ div [ class "inline-flex items-center" ]
 | 
			
		||||
                [ i
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( "mr-2 text-lg", True )
 | 
			
		||||
                        , ( "fa fa-file font-thin", isIdle model file )
 | 
			
		||||
                        , ( "fa fa-spinner animate-spin ", isLoading model file )
 | 
			
		||||
                        , ( "fa fa-check ", isCompleted model file )
 | 
			
		||||
                        , ( "fa fa-bolt", isError model file )
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , div [ class "middle aligned content" ]
 | 
			
		||||
                    [ div [ class "header" ]
 | 
			
		||||
                        [ text name
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex-grow inline-flex justify-end" ]
 | 
			
		||||
                [ text size
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "h-4" ]
 | 
			
		||||
            [ Comp.Progress.progress2 getProgress
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isSuccessAll : Model -> Bool
 | 
			
		||||
isSuccessAll model =
 | 
			
		||||
    List.map makeFileId model.selectedFiles
 | 
			
		||||
        |> List.all (\id -> Set.member id model.completed)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isIdle : Model -> File -> Bool
 | 
			
		||||
isIdle model file =
 | 
			
		||||
    not (isLoading model file || isCompleted model file || isError model file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isLoading : Model -> File -> Bool
 | 
			
		||||
isLoading model file =
 | 
			
		||||
    Dict.member (makeFileId file) model.loading
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isCompleted : Model -> File -> Bool
 | 
			
		||||
isCompleted model file =
 | 
			
		||||
    Set.member (makeFileId file) model.completed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isError : Model -> File -> Bool
 | 
			
		||||
isError model file =
 | 
			
		||||
    Set.member (makeFileId file) model.errored
 | 
			
		||||
							
								
								
									
										429
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/EditForm.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/EditForm.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,429 @@
 | 
			
		||||
module Comp.ItemDetail.EditForm exposing (formTabs, view2)
 | 
			
		||||
 | 
			
		||||
import Comp.CustomFieldMultiInput
 | 
			
		||||
import Comp.DatePicker
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.ItemDetail.FieldTabState as FTabState
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( Model
 | 
			
		||||
        , Msg(..)
 | 
			
		||||
        , NotesField(..)
 | 
			
		||||
        , SaveNameState(..)
 | 
			
		||||
        , personMatchesOrg
 | 
			
		||||
        )
 | 
			
		||||
import Comp.KeyInput
 | 
			
		||||
import Comp.Tabs as TB
 | 
			
		||||
import Data.DropdownStyle
 | 
			
		||||
import Data.Fields
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Dict
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Markdown
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Folder
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        keyAttr =
 | 
			
		||||
            if settings.itemDetailShortcuts then
 | 
			
		||||
                Comp.KeyInput.eventsM KeyInputMsg
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                []
 | 
			
		||||
 | 
			
		||||
        tabStyle =
 | 
			
		||||
            TB.searchMenuStyle
 | 
			
		||||
 | 
			
		||||
        tabs =
 | 
			
		||||
            formTabs settings model
 | 
			
		||||
 | 
			
		||||
        allTabNames =
 | 
			
		||||
            List.map .title tabs
 | 
			
		||||
                |> Set.fromList
 | 
			
		||||
    in
 | 
			
		||||
    div (class "flex flex-col relative" :: keyAttr)
 | 
			
		||||
        [ TB.akkordion tabStyle
 | 
			
		||||
            (tabState settings allTabNames model)
 | 
			
		||||
            tabs
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
formTabs : UiSettings -> Model -> List (TB.Tab Msg)
 | 
			
		||||
formTabs settings model =
 | 
			
		||||
    let
 | 
			
		||||
        dds =
 | 
			
		||||
            Data.DropdownStyle.sidebarStyle
 | 
			
		||||
 | 
			
		||||
        addIconLink tip m =
 | 
			
		||||
            a
 | 
			
		||||
                [ class "float-right"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , title tip
 | 
			
		||||
                , onClick m
 | 
			
		||||
                , class S.link
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-plus" ] []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        editIconLink tip dm m =
 | 
			
		||||
            a
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "hidden", Comp.Dropdown.notSelected dm )
 | 
			
		||||
                    ]
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , class "float-right mr-2"
 | 
			
		||||
                , class S.link
 | 
			
		||||
                , title tip
 | 
			
		||||
                , onClick m
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-pencil-alt" ] []
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        fieldVisible field =
 | 
			
		||||
            Data.UiSettings.fieldVisible settings field
 | 
			
		||||
 | 
			
		||||
        customFieldSettings =
 | 
			
		||||
            Comp.CustomFieldMultiInput.ViewSettings
 | 
			
		||||
                True
 | 
			
		||||
                "field"
 | 
			
		||||
                (\f -> Dict.get f.id model.customFieldSavingIcon)
 | 
			
		||||
 | 
			
		||||
        optional fields html =
 | 
			
		||||
            if
 | 
			
		||||
                List.map fieldVisible fields
 | 
			
		||||
                    |> List.foldl (||) False
 | 
			
		||||
            then
 | 
			
		||||
                html
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                span [ class "invisible hidden" ] []
 | 
			
		||||
    in
 | 
			
		||||
    [ { title = "Name"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "relative mb-4" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , value model.nameModel
 | 
			
		||||
                    , onInput SetName
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    , class "pr-10"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class S.inputLeftIconOnly ]
 | 
			
		||||
                    [ i
 | 
			
		||||
                        [ classList
 | 
			
		||||
                            [ ( "text-green-500 fa fa-check", model.nameState == SaveSuccess )
 | 
			
		||||
                            , ( "text-red-500 fa fa-exclamation-triangle", model.nameState == SaveFailed )
 | 
			
		||||
                            , ( "sync fa fa-circle-notch animate-spin", model.nameState == Saving )
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Date"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ div [ class "relative" ]
 | 
			
		||||
                    [ Html.map ItemDatePickerMsg
 | 
			
		||||
                        (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                            model.itemDate
 | 
			
		||||
                            model.itemDatePicker
 | 
			
		||||
                        )
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ class "ui icon button"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , class S.inputLeftIconLinkSidebar
 | 
			
		||||
                        , onClick RemoveDate
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Icons.dateIcon2 S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                , renderItemDateSuggestions model
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Tags"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ Html.map TagDropdownMsg (Comp.Dropdown.view2 dds settings model.tagModel)
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Folder"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ Html.map FolderDropdownMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        dds
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.folderModel
 | 
			
		||||
                    )
 | 
			
		||||
                , div
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( S.message, True )
 | 
			
		||||
                        , ( "hidden", isFolderMember model )
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ Markdown.toHtml [] """
 | 
			
		||||
You are **not a member** of this folder. This item will be **hidden**
 | 
			
		||||
from any search now. Use a folder where you are a member of to make this
 | 
			
		||||
item visible. This message will disappear then.
 | 
			
		||||
                      """
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Custom Fields"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ Html.map CustomFieldMsg
 | 
			
		||||
                    (Comp.CustomFieldMultiInput.view2
 | 
			
		||||
                        dds
 | 
			
		||||
                        customFieldSettings
 | 
			
		||||
                        model.customFieldsModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Due Date"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ div [ class "relative" ]
 | 
			
		||||
                    [ Html.map DueDatePickerMsg
 | 
			
		||||
                        (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                            model.dueDate
 | 
			
		||||
                            model.dueDatePicker
 | 
			
		||||
                        )
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ class "ui icon button"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , class S.inputLeftIconLinkSidebar
 | 
			
		||||
                        , onClick RemoveDueDate
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Icons.dueDateIcon2 S.dateInputIcon
 | 
			
		||||
                    ]
 | 
			
		||||
                , renderDueDateSuggestions model
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Correspondent"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ optional [ Data.Fields.CorrOrg ] <|
 | 
			
		||||
                div [ class "mb-4" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ Icons.organizationIcon2 "mr-2"
 | 
			
		||||
                        , text "Organization"
 | 
			
		||||
                        , addIconLink "Add new organization" StartCorrOrgModal
 | 
			
		||||
                        , editIconLink "Edit organization" model.corrOrgModel StartEditCorrOrgModal
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Html.map OrgDropdownMsg (Comp.Dropdown.view2 dds settings model.corrOrgModel)
 | 
			
		||||
                    , renderOrgSuggestions model
 | 
			
		||||
                    ]
 | 
			
		||||
            , optional [ Data.Fields.CorrPerson ] <|
 | 
			
		||||
                div [ class "mb-4" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ Icons.personIcon2 "mr-2"
 | 
			
		||||
                        , text "Person"
 | 
			
		||||
                        , addIconLink "Add new correspondent person" StartCorrPersonModal
 | 
			
		||||
                        , editIconLink "Edit person"
 | 
			
		||||
                            model.corrPersonModel
 | 
			
		||||
                            (StartEditPersonModal model.corrPersonModel)
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Html.map CorrPersonMsg (Comp.Dropdown.view2 dds settings model.corrPersonModel)
 | 
			
		||||
                    , renderCorrPersonSuggestions model
 | 
			
		||||
                    , div
 | 
			
		||||
                        [ classList
 | 
			
		||||
                            [ ( "hidden", personMatchesOrg model )
 | 
			
		||||
                            ]
 | 
			
		||||
                        , class S.message
 | 
			
		||||
                        , class "my-2"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-info mr-2 " ] []
 | 
			
		||||
                        , text "The selected person doesn't belong to the selected organization."
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Concerning"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ optional [ Data.Fields.ConcPerson ] <|
 | 
			
		||||
                div [ class "mb-4" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ Icons.personIcon2 "mr-2"
 | 
			
		||||
                        , text "Person"
 | 
			
		||||
                        , addIconLink "Add new concerning person" StartConcPersonModal
 | 
			
		||||
                        , editIconLink "Edit person"
 | 
			
		||||
                            model.concPersonModel
 | 
			
		||||
                            (StartEditPersonModal model.concPersonModel)
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Html.map ConcPersonMsg
 | 
			
		||||
                        (Comp.Dropdown.view2
 | 
			
		||||
                            dds
 | 
			
		||||
                            settings
 | 
			
		||||
                            model.concPersonModel
 | 
			
		||||
                        )
 | 
			
		||||
                    , renderConcPersonSuggestions model
 | 
			
		||||
                    ]
 | 
			
		||||
            , optional [ Data.Fields.ConcEquip ] <|
 | 
			
		||||
                div [ class "mb-4" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ Icons.equipmentIcon2 "mr-2"
 | 
			
		||||
                        , text "Equipment"
 | 
			
		||||
                        , addIconLink "Add new equipment" StartEquipModal
 | 
			
		||||
                        , editIconLink "Edit equipment"
 | 
			
		||||
                            model.concEquipModel
 | 
			
		||||
                            StartEditEquipModal
 | 
			
		||||
                        ]
 | 
			
		||||
                    , Html.map ConcEquipMsg
 | 
			
		||||
                        (Comp.Dropdown.view2
 | 
			
		||||
                            dds
 | 
			
		||||
                            settings
 | 
			
		||||
                            model.concEquipModel
 | 
			
		||||
                        )
 | 
			
		||||
                    , renderConcEquipSuggestions model
 | 
			
		||||
                    ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Direction"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ Html.map DirDropdownMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        dds
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.directionModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderSuggestions : Model -> (a -> String) -> List a -> (a -> Msg) -> Html Msg
 | 
			
		||||
renderSuggestions model mkName idnames tagger =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden", model.item.state /= "created" )
 | 
			
		||||
            ]
 | 
			
		||||
        , class "flex flex-col text-sm"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "font-bold my-1" ]
 | 
			
		||||
            [ text "Suggestions"
 | 
			
		||||
            ]
 | 
			
		||||
        , ul [ class "list-disc ml-6" ] <|
 | 
			
		||||
            (idnames
 | 
			
		||||
                |> List.map
 | 
			
		||||
                    (\p ->
 | 
			
		||||
                        li []
 | 
			
		||||
                            [ a
 | 
			
		||||
                                [ class S.link
 | 
			
		||||
                                , href "#"
 | 
			
		||||
                                , onClick (tagger p)
 | 
			
		||||
                                ]
 | 
			
		||||
                                [ text (mkName p) ]
 | 
			
		||||
                            ]
 | 
			
		||||
                    )
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderOrgSuggestions : Model -> Html Msg
 | 
			
		||||
renderOrgSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        .name
 | 
			
		||||
        (List.take 6 model.itemProposals.corrOrg)
 | 
			
		||||
        SetCorrOrgSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderCorrPersonSuggestions : Model -> Html Msg
 | 
			
		||||
renderCorrPersonSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        .name
 | 
			
		||||
        (List.take 6 model.itemProposals.corrPerson)
 | 
			
		||||
        SetCorrPersonSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderConcPersonSuggestions : Model -> Html Msg
 | 
			
		||||
renderConcPersonSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        .name
 | 
			
		||||
        (List.take 6 model.itemProposals.concPerson)
 | 
			
		||||
        SetConcPersonSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderConcEquipSuggestions : Model -> Html Msg
 | 
			
		||||
renderConcEquipSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        .name
 | 
			
		||||
        (List.take 6 model.itemProposals.concEquipment)
 | 
			
		||||
        SetConcEquipSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderItemDateSuggestions : Model -> Html Msg
 | 
			
		||||
renderItemDateSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        Util.Time.formatDate
 | 
			
		||||
        (List.take 6 model.itemProposals.itemDate)
 | 
			
		||||
        SetItemDateSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderDueDateSuggestions : Model -> Html Msg
 | 
			
		||||
renderDueDateSuggestions model =
 | 
			
		||||
    renderSuggestions model
 | 
			
		||||
        Util.Time.formatDate
 | 
			
		||||
        (List.take 6 model.itemProposals.dueDate)
 | 
			
		||||
        SetDueDateSuggestion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isFolderMember : Model -> Bool
 | 
			
		||||
isFolderMember model =
 | 
			
		||||
    let
 | 
			
		||||
        selected =
 | 
			
		||||
            Comp.Dropdown.getSelected model.folderModel
 | 
			
		||||
                |> List.head
 | 
			
		||||
                |> Maybe.map .id
 | 
			
		||||
    in
 | 
			
		||||
    Util.Folder.isFolderMember model.allFolders selected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tabState : UiSettings -> Set String -> Model -> TB.Tab Msg -> ( TB.State, Msg )
 | 
			
		||||
tabState settings allNames model =
 | 
			
		||||
    let
 | 
			
		||||
        openTabs =
 | 
			
		||||
            if model.item.state == "created" then
 | 
			
		||||
                allNames
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                model.editMenuTabsOpen
 | 
			
		||||
    in
 | 
			
		||||
    FTabState.tabState settings
 | 
			
		||||
        openTabs
 | 
			
		||||
        model.customFieldsModel
 | 
			
		||||
        (.title >> ToggleAkkordionTab)
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
module Comp.ItemDetail.FieldTabState exposing (tabState)
 | 
			
		||||
 | 
			
		||||
import Comp.CustomFieldMultiInput
 | 
			
		||||
import Comp.Tabs as TB
 | 
			
		||||
import Data.Fields
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tabState :
 | 
			
		||||
    UiSettings
 | 
			
		||||
    -> Set String
 | 
			
		||||
    -> Comp.CustomFieldMultiInput.Model
 | 
			
		||||
    -> (TB.Tab msg -> msg)
 | 
			
		||||
    -> TB.Tab msg
 | 
			
		||||
    -> ( TB.State, msg )
 | 
			
		||||
tabState settings openTabs cfmodel toggle tab =
 | 
			
		||||
    let
 | 
			
		||||
        isHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        hidden =
 | 
			
		||||
            case tab.title of
 | 
			
		||||
                "Tags" ->
 | 
			
		||||
                    isHidden Data.Fields.Tag
 | 
			
		||||
 | 
			
		||||
                "Folder" ->
 | 
			
		||||
                    isHidden Data.Fields.Folder
 | 
			
		||||
 | 
			
		||||
                "Correspondent" ->
 | 
			
		||||
                    isHidden Data.Fields.CorrOrg && isHidden Data.Fields.CorrPerson
 | 
			
		||||
 | 
			
		||||
                "Concerning" ->
 | 
			
		||||
                    isHidden Data.Fields.ConcEquip && isHidden Data.Fields.ConcPerson
 | 
			
		||||
 | 
			
		||||
                "Custom Fields" ->
 | 
			
		||||
                    isHidden Data.Fields.CustomFields
 | 
			
		||||
                        || Comp.CustomFieldMultiInput.isEmpty cfmodel
 | 
			
		||||
 | 
			
		||||
                "Date" ->
 | 
			
		||||
                    isHidden Data.Fields.Date
 | 
			
		||||
 | 
			
		||||
                "Due Date" ->
 | 
			
		||||
                    isHidden Data.Fields.DueDate
 | 
			
		||||
 | 
			
		||||
                "Direction" ->
 | 
			
		||||
                    isHidden Data.Fields.Direction
 | 
			
		||||
 | 
			
		||||
                _ ->
 | 
			
		||||
                    False
 | 
			
		||||
 | 
			
		||||
        state =
 | 
			
		||||
            if hidden then
 | 
			
		||||
                TB.Hidden
 | 
			
		||||
 | 
			
		||||
            else if Set.member tab.title openTabs then
 | 
			
		||||
                TB.Open
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                TB.Closed
 | 
			
		||||
    in
 | 
			
		||||
    ( state, toggle tab )
 | 
			
		||||
							
								
								
									
										198
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/ItemInfoHeader.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/ItemInfoHeader.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
module Comp.ItemDetail.ItemInfoHeader exposing (view)
 | 
			
		||||
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( Model
 | 
			
		||||
        , Msg(..)
 | 
			
		||||
        , NotesField(..)
 | 
			
		||||
        , SaveNameState(..)
 | 
			
		||||
        )
 | 
			
		||||
import Comp.LinkTarget
 | 
			
		||||
import Data.Direction
 | 
			
		||||
import Data.Fields
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    let
 | 
			
		||||
        date =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class "ml-2 sm:ml-0 whitespace-nowrap py-1 whitespace-nowrap opacity-75"
 | 
			
		||||
                , title "Item Date"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.dateIcon2 "mr-2"
 | 
			
		||||
                , Maybe.withDefault model.item.created model.item.itemDate
 | 
			
		||||
                    |> Util.Time.formatDate
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.Date
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        itemStyle =
 | 
			
		||||
            "ml-2 sm:ml-4 py-1 whitespace-nowrap "
 | 
			
		||||
 | 
			
		||||
        linkStyle =
 | 
			
		||||
            "opacity-75 hover:opacity-100"
 | 
			
		||||
 | 
			
		||||
        duedate =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class "ml-2 sm:ml-4 py-1 max-w-min whitespace-nowrap opacity-100"
 | 
			
		||||
                , class S.basicLabel
 | 
			
		||||
                , title "Due Date"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.dueDateIcon2 "mr-2"
 | 
			
		||||
                , Maybe.map Util.Time.formatDate model.item.dueDate
 | 
			
		||||
                    |> Maybe.withDefault ""
 | 
			
		||||
                    |> text
 | 
			
		||||
                ]
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.DueDate
 | 
			
		||||
                && Util.Maybe.nonEmpty model.item.dueDate
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        corr =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class itemStyle
 | 
			
		||||
                , title "Correspondent"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.correspondentIcon2 "mr-2"
 | 
			
		||||
                    :: Comp.LinkTarget.makeCorrLink model.item
 | 
			
		||||
                        [ ( linkStyle, True ) ]
 | 
			
		||||
                        SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.CorrOrg
 | 
			
		||||
                || Data.UiSettings.fieldVisible settings Data.Fields.CorrPerson
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        conc =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class itemStyle
 | 
			
		||||
                , title "Concerning"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.concernedIcon2 "mr-2"
 | 
			
		||||
                    :: Comp.LinkTarget.makeConcLink model.item
 | 
			
		||||
                        [ ( linkStyle, True ) ]
 | 
			
		||||
                        SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.ConcEquip
 | 
			
		||||
                || Data.UiSettings.fieldVisible settings Data.Fields.ConcPerson
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        itemfolder =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class itemStyle
 | 
			
		||||
                , title "Folder"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.folderIcon2 "mr-2"
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink model.item
 | 
			
		||||
                    [ ( linkStyle, True ) ]
 | 
			
		||||
                    SetLinkTarget
 | 
			
		||||
                ]
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.Folder
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        src =
 | 
			
		||||
            ( div
 | 
			
		||||
                [ class itemStyle
 | 
			
		||||
                , title "Source"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.sourceIcon2 "mr-2"
 | 
			
		||||
                , Comp.LinkTarget.makeSourceLink [ ( linkStyle, True ) ]
 | 
			
		||||
                    SetLinkTarget
 | 
			
		||||
                    model.item.source
 | 
			
		||||
                ]
 | 
			
		||||
            , True
 | 
			
		||||
            )
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col pb-2" ]
 | 
			
		||||
        [ div [ class "flex flex-row items-center text-2xl" ]
 | 
			
		||||
            [ i
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "hidden", Data.UiSettings.fieldHidden settings Data.Fields.Direction )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class (Data.Direction.iconFromString2 model.item.direction)
 | 
			
		||||
                , class "mr-2"
 | 
			
		||||
                , title model.item.direction
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div [ class "flex-grow ml-1 flex flex-col" ]
 | 
			
		||||
                [ div [ class "flex flex-row items-center font-semibold" ]
 | 
			
		||||
                    [ text model.item.name
 | 
			
		||||
                    , div
 | 
			
		||||
                        [ classList
 | 
			
		||||
                            [ ( "hidden", model.item.state /= "created" )
 | 
			
		||||
                            ]
 | 
			
		||||
                        , class "ml-3 text-base label bg-blue-500 dark:bg-lightblue-500 text-white rounded-lg"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ text "New"
 | 
			
		||||
                        , i [ class "fa fa-exclamation ml-2" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , ul [ class "flex flex-col sm:flex-row flex-wrap text-base " ]
 | 
			
		||||
            (List.filter Tuple.second
 | 
			
		||||
                [ date
 | 
			
		||||
                , corr
 | 
			
		||||
                , conc
 | 
			
		||||
                , itemfolder
 | 
			
		||||
                , src
 | 
			
		||||
                , duedate
 | 
			
		||||
                ]
 | 
			
		||||
                |> List.map Tuple.first
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "font-semibold mb-2 mt-3 pr-3" ]
 | 
			
		||||
            (renderTagsAndFields settings model)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderTagsAndFields : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
renderTagsAndFields settings model =
 | 
			
		||||
    [ div [ class "flex flex-row flex-wrap items-center sm:justify-end" ]
 | 
			
		||||
        (renderTags settings model ++ renderCustomValues settings model)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderTags : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
renderTags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        tagView t =
 | 
			
		||||
            Comp.LinkTarget.makeTagLink
 | 
			
		||||
                (IdName t.id t.name)
 | 
			
		||||
                [ ( "label inline-flex ml-2 hover:opacity-90 mt-1 items-center", True )
 | 
			
		||||
                , ( Data.UiSettings.tagColorString2 t settings, True )
 | 
			
		||||
                ]
 | 
			
		||||
                SetLinkTarget
 | 
			
		||||
    in
 | 
			
		||||
    if Data.UiSettings.fieldHidden settings Data.Fields.Tag || model.item.tags == [] then
 | 
			
		||||
        []
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.map tagView model.item.tags
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderCustomValues : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
renderCustomValues settings model =
 | 
			
		||||
    let
 | 
			
		||||
        fieldView cv =
 | 
			
		||||
            Comp.LinkTarget.makeCustomFieldLink2
 | 
			
		||||
                cv
 | 
			
		||||
                [ ( "ml-2 hover:opacity-90 mt-1 " ++ S.basicLabel, True ) ]
 | 
			
		||||
                SetLinkTarget
 | 
			
		||||
 | 
			
		||||
        labelThenName cv =
 | 
			
		||||
            Maybe.withDefault cv.name cv.label
 | 
			
		||||
    in
 | 
			
		||||
    if Data.UiSettings.fieldHidden settings Data.Fields.CustomFields || model.item.customfields == [] then
 | 
			
		||||
        []
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.map fieldView (List.sortBy labelThenName model.item.customfields)
 | 
			
		||||
@@ -103,6 +103,8 @@ type alias Model =
 | 
			
		||||
    , customFieldThrottle : Throttle Msg
 | 
			
		||||
    , allTags : List Tag
 | 
			
		||||
    , allPersons : Dict String Person
 | 
			
		||||
    , attachmentDropdownOpen : Bool
 | 
			
		||||
    , editMenuTabsOpen : Set String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -134,7 +136,7 @@ emptyModel =
 | 
			
		||||
    , attachMenuOpen = False
 | 
			
		||||
    , menuOpen = False
 | 
			
		||||
    , tagModel =
 | 
			
		||||
        Util.Tag.makeDropdownModel
 | 
			
		||||
        Util.Tag.makeDropdownModel2
 | 
			
		||||
    , directionModel =
 | 
			
		||||
        Comp.Dropdown.makeSingleList
 | 
			
		||||
            { makeOption =
 | 
			
		||||
@@ -195,7 +197,7 @@ emptyModel =
 | 
			
		||||
    , pdfNativeView = Nothing
 | 
			
		||||
    , deleteAttachConfirm = Comp.YesNoDimmer.emptyModel
 | 
			
		||||
    , addFilesOpen = False
 | 
			
		||||
    , addFilesModel = Comp.Dropzone.init Comp.Dropzone.defaultSettings
 | 
			
		||||
    , addFilesModel = Comp.Dropzone.init []
 | 
			
		||||
    , selectedFiles = []
 | 
			
		||||
    , completed = Set.empty
 | 
			
		||||
    , errored = Set.empty
 | 
			
		||||
@@ -209,6 +211,8 @@ emptyModel =
 | 
			
		||||
    , customFieldThrottle = Throttle.create 1
 | 
			
		||||
    , allTags = []
 | 
			
		||||
    , allPersons = Dict.empty
 | 
			
		||||
    , attachmentDropdownOpen = False
 | 
			
		||||
    , editMenuTabsOpen = Set.empty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -297,6 +301,9 @@ type Msg
 | 
			
		||||
    | CustomFieldMsg Comp.CustomFieldMultiInput.Msg
 | 
			
		||||
    | CustomFieldSaveResp CustomField String (Result Http.Error BasicResult)
 | 
			
		||||
    | CustomFieldRemoveResp String (Result Http.Error BasicResult)
 | 
			
		||||
    | ToggleAttachmentDropdown
 | 
			
		||||
    | ToggleAkkordionTab String
 | 
			
		||||
    | ToggleOpenAllAkkordionTabs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type SaveNameState
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
module Comp.ItemDetail.EditMenu exposing
 | 
			
		||||
module Comp.ItemDetail.MultiEditMenu exposing
 | 
			
		||||
    ( Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , SaveNameState(..)
 | 
			
		||||
@@ -7,6 +7,7 @@ module Comp.ItemDetail.EditMenu exposing
 | 
			
		||||
    , loadModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -14,7 +15,6 @@ import Api.Model.EquipmentList exposing (EquipmentList)
 | 
			
		||||
import Api.Model.FolderItem exposing (FolderItem)
 | 
			
		||||
import Api.Model.FolderList exposing (FolderList)
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.ItemProposals exposing (ItemProposals)
 | 
			
		||||
import Api.Model.PersonList exposing (PersonList)
 | 
			
		||||
import Api.Model.ReferenceList exposing (ReferenceList)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
@@ -23,9 +23,12 @@ import Comp.CustomFieldMultiInput
 | 
			
		||||
import Comp.DatePicker
 | 
			
		||||
import Comp.DetailEdit
 | 
			
		||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
 | 
			
		||||
import Comp.ItemDetail.FieldTabState as FTabState
 | 
			
		||||
import Comp.ItemDetail.FormChange exposing (FormChange(..))
 | 
			
		||||
import Comp.Tabs as TB
 | 
			
		||||
import Data.CustomFieldChange exposing (CustomFieldChange(..))
 | 
			
		||||
import Data.Direction exposing (Direction)
 | 
			
		||||
import Data.DropdownStyle
 | 
			
		||||
import Data.Fields
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
@@ -37,6 +40,8 @@ import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Markdown
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Task
 | 
			
		||||
import Throttle exposing (Throttle)
 | 
			
		||||
import Time
 | 
			
		||||
@@ -71,7 +76,6 @@ type alias Model =
 | 
			
		||||
    , directionModel : Comp.Dropdown.Model Direction
 | 
			
		||||
    , itemDatePicker : DatePicker
 | 
			
		||||
    , itemDate : Maybe Int
 | 
			
		||||
    , itemProposals : ItemProposals
 | 
			
		||||
    , dueDate : Maybe Int
 | 
			
		||||
    , dueDatePicker : DatePicker
 | 
			
		||||
    , corrOrgModel : Comp.Dropdown.Model IdName
 | 
			
		||||
@@ -81,6 +85,7 @@ type alias Model =
 | 
			
		||||
    , modalEdit : Maybe Comp.DetailEdit.Model
 | 
			
		||||
    , tagEditMode : TagEditMode
 | 
			
		||||
    , customFieldModel : Comp.CustomFieldMultiInput.Model
 | 
			
		||||
    , openTabs : Set String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -107,12 +112,13 @@ type Msg
 | 
			
		||||
    | GetEquipResp (Result Http.Error EquipmentList)
 | 
			
		||||
    | GetFolderResp (Result Http.Error FolderList)
 | 
			
		||||
    | CustomFieldMsg Comp.CustomFieldMultiInput.Msg
 | 
			
		||||
    | ToggleAkkordionTab String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
init : Model
 | 
			
		||||
init =
 | 
			
		||||
    { tagModel =
 | 
			
		||||
        Util.Tag.makeDropdownModel
 | 
			
		||||
        Util.Tag.makeDropdownModel2
 | 
			
		||||
    , directionModel =
 | 
			
		||||
        Comp.Dropdown.makeSingleList
 | 
			
		||||
            { makeOption =
 | 
			
		||||
@@ -155,12 +161,12 @@ init =
 | 
			
		||||
    , nameSaveThrottle = Throttle.create 1
 | 
			
		||||
    , itemDatePicker = Comp.DatePicker.emptyModel
 | 
			
		||||
    , itemDate = Nothing
 | 
			
		||||
    , itemProposals = Api.Model.ItemProposals.empty
 | 
			
		||||
    , dueDate = Nothing
 | 
			
		||||
    , dueDatePicker = Comp.DatePicker.emptyModel
 | 
			
		||||
    , modalEdit = Nothing
 | 
			
		||||
    , tagEditMode = AddTags
 | 
			
		||||
    , customFieldModel = Comp.CustomFieldMultiInput.initWith []
 | 
			
		||||
    , openTabs = Set.empty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -563,7 +569,7 @@ update flags msg model =
 | 
			
		||||
        CustomFieldMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                res =
 | 
			
		||||
                    Comp.CustomFieldMultiInput.update lm model.customFieldModel
 | 
			
		||||
                    Comp.CustomFieldMultiInput.update flags lm model.customFieldModel
 | 
			
		||||
 | 
			
		||||
                model_ =
 | 
			
		||||
                    { model | customFieldModel = res.model }
 | 
			
		||||
@@ -587,6 +593,17 @@ update flags msg model =
 | 
			
		||||
            in
 | 
			
		||||
            UpdateResult model_ cmd_ Sub.none change
 | 
			
		||||
 | 
			
		||||
        ToggleAkkordionTab title ->
 | 
			
		||||
            let
 | 
			
		||||
                tabs =
 | 
			
		||||
                    if Set.member title model.openTabs then
 | 
			
		||||
                        Set.remove title model.openTabs
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Set.insert title model.openTabs
 | 
			
		||||
            in
 | 
			
		||||
            UpdateResult { model | openTabs = tabs } Cmd.none Sub.none NoFormChange
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
nameThrottleSub : Model -> Sub Msg
 | 
			
		||||
nameThrottleSub model =
 | 
			
		||||
@@ -857,3 +874,286 @@ actionInputDatePicker =
 | 
			
		||||
            Comp.DatePicker.defaultSettings
 | 
			
		||||
    in
 | 
			
		||||
    { ds | containerClassList = [ ( "ui action input", True ) ] }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : ViewConfig -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 =
 | 
			
		||||
    renderEditForm2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderEditForm2 : ViewConfig -> UiSettings -> Model -> Html Msg
 | 
			
		||||
renderEditForm2 cfg settings model =
 | 
			
		||||
    let
 | 
			
		||||
        fieldVisible field =
 | 
			
		||||
            Data.UiSettings.fieldVisible settings field
 | 
			
		||||
 | 
			
		||||
        optional fields html =
 | 
			
		||||
            if
 | 
			
		||||
                List.map fieldVisible fields
 | 
			
		||||
                    |> List.foldl (||) False
 | 
			
		||||
            then
 | 
			
		||||
                html
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                span [ class "hidden" ] []
 | 
			
		||||
 | 
			
		||||
        tagModeIcon =
 | 
			
		||||
            case model.tagEditMode of
 | 
			
		||||
                AddTags ->
 | 
			
		||||
                    i [ class "fa fa-plus" ] []
 | 
			
		||||
 | 
			
		||||
                RemoveTags ->
 | 
			
		||||
                    i [ class "fa fa-eraser" ] []
 | 
			
		||||
 | 
			
		||||
                ReplaceTags ->
 | 
			
		||||
                    i [ class "fa fa-redo-alt" ] []
 | 
			
		||||
 | 
			
		||||
        tagModeMsg =
 | 
			
		||||
            case model.tagEditMode of
 | 
			
		||||
                AddTags ->
 | 
			
		||||
                    "Tags chosen here are *added* to all selected items."
 | 
			
		||||
 | 
			
		||||
                RemoveTags ->
 | 
			
		||||
                    "Tags chosen here are *removed* from all selected items."
 | 
			
		||||
 | 
			
		||||
                ReplaceTags ->
 | 
			
		||||
                    "Tags chosen here *replace* those on selected items."
 | 
			
		||||
 | 
			
		||||
        customFieldIcon field =
 | 
			
		||||
            case cfg.customFieldState field.id of
 | 
			
		||||
                SaveSuccess ->
 | 
			
		||||
                    Nothing
 | 
			
		||||
 | 
			
		||||
                SaveFailed ->
 | 
			
		||||
                    Just "text-red-500 fa fa-exclamation-triangle"
 | 
			
		||||
 | 
			
		||||
                Saving ->
 | 
			
		||||
                    Just "fa fa-sync-alt animate-spin"
 | 
			
		||||
 | 
			
		||||
        customFieldSettings =
 | 
			
		||||
            Comp.CustomFieldMultiInput.ViewSettings
 | 
			
		||||
                False
 | 
			
		||||
                "mb-4"
 | 
			
		||||
                customFieldIcon
 | 
			
		||||
 | 
			
		||||
        dds =
 | 
			
		||||
            Data.DropdownStyle.sidebarStyle
 | 
			
		||||
 | 
			
		||||
        tabStyle =
 | 
			
		||||
            TB.searchMenuStyle
 | 
			
		||||
    in
 | 
			
		||||
    div [ class cfg.menuClass, class "mt-2" ]
 | 
			
		||||
        [ TB.akkordion
 | 
			
		||||
            tabStyle
 | 
			
		||||
            (tabState settings model)
 | 
			
		||||
            [ { title = "Confirm/Unconfirm item metadata"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ div
 | 
			
		||||
                        [ class "flex flex-row space-x-4"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ button
 | 
			
		||||
                            [ class S.primaryButton
 | 
			
		||||
                            , class "flex-grow"
 | 
			
		||||
                            , onClick (ConfirmMsg True)
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ text "Confirm"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , button
 | 
			
		||||
                            [ class S.secondaryButton
 | 
			
		||||
                            , class "flex-grow"
 | 
			
		||||
                            , onClick (ConfirmMsg False)
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ text "Unconfirm"
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Tags"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ div [ class "field" ]
 | 
			
		||||
                        [ label [ class S.inputLabel ]
 | 
			
		||||
                            [ Icons.tagsIcon2 ""
 | 
			
		||||
                            , text "Tags"
 | 
			
		||||
                            , a
 | 
			
		||||
                                [ class "float-right"
 | 
			
		||||
                                , class S.link
 | 
			
		||||
                                , href "#"
 | 
			
		||||
                                , title "Change tag edit mode"
 | 
			
		||||
                                , onClick ToggleTagEditMode
 | 
			
		||||
                                ]
 | 
			
		||||
                                [ tagModeIcon
 | 
			
		||||
                                ]
 | 
			
		||||
                            ]
 | 
			
		||||
                        , Html.map TagDropdownMsg (Comp.Dropdown.view2 dds settings model.tagModel)
 | 
			
		||||
                        , Markdown.toHtml [ class "opacity-50 text-sm" ] tagModeMsg
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Folder"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ Html.map FolderDropdownMsg (Comp.Dropdown.view2 dds settings model.folderModel)
 | 
			
		||||
                    , div
 | 
			
		||||
                        [ classList
 | 
			
		||||
                            [ ( S.message, True )
 | 
			
		||||
                            , ( "hidden", isFolderMember model )
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ Markdown.toHtml [] """
 | 
			
		||||
You are **not a member** of this folder. This item will be **hidden**
 | 
			
		||||
from any search now. Use a folder where you are a member of to make this
 | 
			
		||||
item visible. This message will disappear then.
 | 
			
		||||
                      """
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Custom Fields"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ Html.map CustomFieldMsg
 | 
			
		||||
                        (Comp.CustomFieldMultiInput.view2 dds customFieldSettings model.customFieldModel)
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Date"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ div [ class "relative" ]
 | 
			
		||||
                        [ Html.map ItemDatePickerMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTime
 | 
			
		||||
                                model.itemDate
 | 
			
		||||
                                actionInputDatePicker2
 | 
			
		||||
                                model.itemDatePicker
 | 
			
		||||
                            )
 | 
			
		||||
                        , a
 | 
			
		||||
                            [ class S.inputLeftIconLinkSidebar
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            , onClick RemoveDate
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
                            ]
 | 
			
		||||
                        , Icons.dateIcon2 S.dateInputIcon
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Due Date"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ div [ class "relative" ]
 | 
			
		||||
                        [ Html.map DueDatePickerMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTime
 | 
			
		||||
                                model.dueDate
 | 
			
		||||
                                actionInputDatePicker2
 | 
			
		||||
                                model.dueDatePicker
 | 
			
		||||
                            )
 | 
			
		||||
                        , a
 | 
			
		||||
                            [ class S.inputLeftIconLinkSidebar
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            , onClick RemoveDueDate
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-trash-alt font-thin" ] []
 | 
			
		||||
                            ]
 | 
			
		||||
                        , Icons.dueDateIcon2 S.dateInputIcon
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Correspondent"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ optional [ Data.Fields.CorrOrg ] <|
 | 
			
		||||
                        div [ class "mb-4" ]
 | 
			
		||||
                            [ label [ class S.inputLabel ]
 | 
			
		||||
                                [ Icons.organizationIcon2 ""
 | 
			
		||||
                                , span [ class "ml-2" ]
 | 
			
		||||
                                    [ text "Organization"
 | 
			
		||||
                                    ]
 | 
			
		||||
                                ]
 | 
			
		||||
                            , Html.map OrgDropdownMsg (Comp.Dropdown.view2 dds settings model.corrOrgModel)
 | 
			
		||||
                            ]
 | 
			
		||||
                    , optional [ Data.Fields.CorrPerson ] <|
 | 
			
		||||
                        div [ class "mb-4" ]
 | 
			
		||||
                            [ label [ class S.inputLabel ]
 | 
			
		||||
                                [ Icons.personIcon2 ""
 | 
			
		||||
                                , span [ class "ml-2" ]
 | 
			
		||||
                                    [ text "Person"
 | 
			
		||||
                                    ]
 | 
			
		||||
                                ]
 | 
			
		||||
                            , Html.map CorrPersonMsg (Comp.Dropdown.view2 dds settings model.corrPersonModel)
 | 
			
		||||
                            ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title =
 | 
			
		||||
                    "Concerning"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ optional [ Data.Fields.ConcPerson ] <|
 | 
			
		||||
                        div [ class "mb-4" ]
 | 
			
		||||
                            [ label [ class S.inputLabel ]
 | 
			
		||||
                                [ Icons.personIcon2 ""
 | 
			
		||||
                                , span [ class "ml-2" ]
 | 
			
		||||
                                    [ text "Person" ]
 | 
			
		||||
                                ]
 | 
			
		||||
                            , Html.map ConcPersonMsg (Comp.Dropdown.view2 dds settings model.concPersonModel)
 | 
			
		||||
                            ]
 | 
			
		||||
                    , optional [ Data.Fields.ConcEquip ] <|
 | 
			
		||||
                        div [ class "mb-4" ]
 | 
			
		||||
                            [ label [ class S.inputLabel ]
 | 
			
		||||
                                [ Icons.equipmentIcon2 ""
 | 
			
		||||
                                , span [ class "ml-2" ]
 | 
			
		||||
                                    [ text "Equipment" ]
 | 
			
		||||
                                ]
 | 
			
		||||
                            , Html.map ConcEquipMsg (Comp.Dropdown.view2 dds settings model.concEquipModel)
 | 
			
		||||
                            ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Direction"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ Html.map DirDropdownMsg (Comp.Dropdown.view2 dds settings model.directionModel)
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            , { title = "Name"
 | 
			
		||||
              , info = Nothing
 | 
			
		||||
              , body =
 | 
			
		||||
                    [ div [ class "relative" ]
 | 
			
		||||
                        [ input
 | 
			
		||||
                            [ type_ "text"
 | 
			
		||||
                            , value model.nameModel
 | 
			
		||||
                            , onInput SetName
 | 
			
		||||
                            , class S.textInputSidebar
 | 
			
		||||
                            ]
 | 
			
		||||
                            []
 | 
			
		||||
                        , span [ class S.inputLeftIconOnly ]
 | 
			
		||||
                            [ i
 | 
			
		||||
                                [ classList
 | 
			
		||||
                                    [ ( "text-green-500 fa fa-check", cfg.nameState == SaveSuccess )
 | 
			
		||||
                                    , ( "text-red-500 fa fa-exclamation-triangle", cfg.nameState == SaveFailed )
 | 
			
		||||
                                    , ( "sync fa fa-circle-notch animate-spin", cfg.nameState == Saving )
 | 
			
		||||
                                    ]
 | 
			
		||||
                                ]
 | 
			
		||||
                                []
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tabState : UiSettings -> Model -> TB.Tab Msg -> ( TB.State, Msg )
 | 
			
		||||
tabState settings model tab =
 | 
			
		||||
    FTabState.tabState settings
 | 
			
		||||
        model.openTabs
 | 
			
		||||
        model.customFieldModel
 | 
			
		||||
        (.title >> ToggleAkkordionTab)
 | 
			
		||||
        tab
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
actionInputDatePicker2 : DatePicker.Settings
 | 
			
		||||
actionInputDatePicker2 =
 | 
			
		||||
    Comp.DatePicker.defaultSettings
 | 
			
		||||
							
								
								
									
										101
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/Notes.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/Notes.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
module Comp.ItemDetail.Notes exposing (view)
 | 
			
		||||
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( Model
 | 
			
		||||
        , Msg(..)
 | 
			
		||||
        , NotesField(..)
 | 
			
		||||
        , SaveNameState(..)
 | 
			
		||||
        )
 | 
			
		||||
import Comp.MarkdownInput
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Markdown
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    case model.notesField of
 | 
			
		||||
        ViewNotes ->
 | 
			
		||||
            div [ class "flex flex-col ds-item-detail-notes" ]
 | 
			
		||||
                [ div [ class "flex flex-row items-center border-b dark:border-bluegray-600" ]
 | 
			
		||||
                    [ div [ class "flex-grow font-bold text-lg" ]
 | 
			
		||||
                        [ text "Notes"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "" ]
 | 
			
		||||
                        [ a
 | 
			
		||||
                            [ class S.link
 | 
			
		||||
                            , onClick ToggleEditNotes
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-edit mr-2" ] []
 | 
			
		||||
                            , text "Edit"
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "" ]
 | 
			
		||||
                    [ Markdown.toHtml [ class "markdown-preview" ]
 | 
			
		||||
                        (Maybe.withDefault "" model.item.notes)
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        EditNotes mm ->
 | 
			
		||||
            let
 | 
			
		||||
                classes act =
 | 
			
		||||
                    classList
 | 
			
		||||
                        [ ( "opacity-100", act )
 | 
			
		||||
                        , ( "opacity-50", not act )
 | 
			
		||||
                        ]
 | 
			
		||||
            in
 | 
			
		||||
            div [ class "flex flex-col ds-item-detail-notes" ]
 | 
			
		||||
                [ div [ class "flex flex-col" ]
 | 
			
		||||
                    [ div [ class "flex flex-row items-center" ]
 | 
			
		||||
                        [ div [ class "font-bold text-lg" ]
 | 
			
		||||
                            [ text "Notes"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , div [ class "flex flex-grow justify-end text-sm" ]
 | 
			
		||||
                            [ Html.map NotesEditMsg
 | 
			
		||||
                                (Comp.MarkdownInput.viewEditLink2 classes mm)
 | 
			
		||||
                            , span [ class "px-3" ] [ text "•" ]
 | 
			
		||||
                            , Html.map NotesEditMsg
 | 
			
		||||
                                (Comp.MarkdownInput.viewPreviewLink2 classes mm)
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "flex flex-col h-64" ]
 | 
			
		||||
                    [ Html.map NotesEditMsg
 | 
			
		||||
                        (Comp.MarkdownInput.viewContent2
 | 
			
		||||
                            (Maybe.withDefault "" model.notesModel)
 | 
			
		||||
                            mm
 | 
			
		||||
                        )
 | 
			
		||||
                    , div [ class "text-sm flex justify-end" ]
 | 
			
		||||
                        [ Comp.MarkdownInput.viewCheatLink2 S.link mm
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "flex flex-row mt-1" ]
 | 
			
		||||
                        [ a
 | 
			
		||||
                            [ class S.primaryButton
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            , onClick SaveNotes
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-save font-thin mr-2" ] []
 | 
			
		||||
                            , text "Save"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , a
 | 
			
		||||
                            [ classList
 | 
			
		||||
                                [ ( "invisible hidden", Util.String.isNothingOrBlank model.item.notes )
 | 
			
		||||
                                ]
 | 
			
		||||
                            , class S.secondaryButton
 | 
			
		||||
                            , class "ml-2"
 | 
			
		||||
                            , href "#"
 | 
			
		||||
                            , onClick ToggleEditNotes
 | 
			
		||||
                            ]
 | 
			
		||||
                            [ i [ class "fa fa-times mr-2" ] []
 | 
			
		||||
                            , text "Cancel"
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
							
								
								
									
										343
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/SingleAttachment.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										343
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/SingleAttachment.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,343 @@
 | 
			
		||||
module Comp.ItemDetail.SingleAttachment exposing (view)
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.Attachment exposing (Attachment)
 | 
			
		||||
import Comp.AttachmentMeta
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( Model
 | 
			
		||||
        , Msg(..)
 | 
			
		||||
        , NotesField(..)
 | 
			
		||||
        , SaveNameState(..)
 | 
			
		||||
        )
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Dict
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Html5.DragDrop as DD
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Size
 | 
			
		||||
import Util.String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Int -> Attachment -> Html Msg
 | 
			
		||||
view settings model pos attach =
 | 
			
		||||
    let
 | 
			
		||||
        fileUrl =
 | 
			
		||||
            Api.fileURL attach.id
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col md:relative h-full mb-2"
 | 
			
		||||
        , classList
 | 
			
		||||
            [ ( "hidden", not (attachmentVisible model pos) )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map (DeleteAttachConfirm attach.id)
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                (Comp.YesNoDimmer.defaultSettings2 "Really delete this file?")
 | 
			
		||||
                model.deleteAttachConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "flex flex-row px-2 py-2 text-sm"
 | 
			
		||||
            , class S.border
 | 
			
		||||
            ]
 | 
			
		||||
            [ attachHeader settings model pos attach
 | 
			
		||||
            ]
 | 
			
		||||
        , editAttachmentName model attach
 | 
			
		||||
        , attachmentSelect model pos attach
 | 
			
		||||
        , if isAttachMetaOpen model attach.id then
 | 
			
		||||
            case Dict.get attach.id model.attachMeta of
 | 
			
		||||
                Just am ->
 | 
			
		||||
                    Html.map (AttachMetaMsg attach.id)
 | 
			
		||||
                        (Comp.AttachmentMeta.view2 am)
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    span [ class "hidden" ] []
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            div
 | 
			
		||||
                [ class "flex flex-col relative px-2 pt-2 h-full"
 | 
			
		||||
                , class "border-r border-l border-b dark:border-bluegray-600"
 | 
			
		||||
                , id "ds-pdf-view-parent"
 | 
			
		||||
                , style "max-height" "calc(100vh - 140px)"
 | 
			
		||||
                , style "min-height" "500px"
 | 
			
		||||
                ]
 | 
			
		||||
                [ iframe
 | 
			
		||||
                    [ if Maybe.withDefault settings.nativePdfPreview model.pdfNativeView then
 | 
			
		||||
                        src fileUrl
 | 
			
		||||
 | 
			
		||||
                      else
 | 
			
		||||
                        src (fileUrl ++ "/view")
 | 
			
		||||
                    , class "absolute h-full w-full top-0 left-0 mx-0 py-0"
 | 
			
		||||
                    , id "ds-pdf-view-iframe"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{-| attachment header
 | 
			
		||||
 | 
			
		||||
  - toggle thumbs
 | 
			
		||||
  - name + size
 | 
			
		||||
  - eye icon to open it
 | 
			
		||||
  - menu
 | 
			
		||||
      - rename
 | 
			
		||||
      - meta data
 | 
			
		||||
      - download archive
 | 
			
		||||
      - download
 | 
			
		||||
      - delete
 | 
			
		||||
      - native view
 | 
			
		||||
 | 
			
		||||
-}
 | 
			
		||||
attachHeader : UiSettings -> Model -> Int -> Attachment -> Html Msg
 | 
			
		||||
attachHeader settings model _ attach =
 | 
			
		||||
    let
 | 
			
		||||
        attachName =
 | 
			
		||||
            Maybe.withDefault "No name" attach.name
 | 
			
		||||
 | 
			
		||||
        fileUrl =
 | 
			
		||||
            Api.fileURL attach.id
 | 
			
		||||
 | 
			
		||||
        hasArchive =
 | 
			
		||||
            List.map .id model.item.archives
 | 
			
		||||
                |> List.member attach.id
 | 
			
		||||
 | 
			
		||||
        multiAttach =
 | 
			
		||||
            List.length model.item.attachments > 1
 | 
			
		||||
 | 
			
		||||
        attachSelectToggle mobile =
 | 
			
		||||
            a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , onClick ToggleAttachMenu
 | 
			
		||||
                , class S.secondaryBasicButton
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( "bg-gray-200 dark:bg-bluegray-600 ", model.attachMenuOpen )
 | 
			
		||||
                    , ( "hidden", not multiAttach )
 | 
			
		||||
                    , ( "sm:hidden", multiAttach && mobile )
 | 
			
		||||
                    , ( "hidden sm:block", multiAttach && not mobile )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-images font-thin" ] []
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col sm:flex-row items-center w-full" ]
 | 
			
		||||
        [ attachSelectToggle False
 | 
			
		||||
        , div [ class "ml-2 text-base font-bold flex-grow w-full text-center sm:text-left" ]
 | 
			
		||||
            [ text attachName
 | 
			
		||||
            , text " ("
 | 
			
		||||
            , text (Util.Size.bytesReadable Util.Size.B (toFloat attach.size))
 | 
			
		||||
            , text ")"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row justify-end items-center" ]
 | 
			
		||||
            [ attachSelectToggle True
 | 
			
		||||
            , a
 | 
			
		||||
                [ href fileUrl
 | 
			
		||||
                , target "_new"
 | 
			
		||||
                , title "Open file in new tab"
 | 
			
		||||
                , class S.secondaryBasicButton
 | 
			
		||||
                , class "ml-2"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-eye font-thin" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            , MB.viewItem <|
 | 
			
		||||
                MB.Dropdown
 | 
			
		||||
                    { linkIcon = "fa fa-bars"
 | 
			
		||||
                    , linkClass =
 | 
			
		||||
                        [ ( "ml-2", True )
 | 
			
		||||
                        , ( S.secondaryBasicButton, True )
 | 
			
		||||
                        ]
 | 
			
		||||
                    , toggleMenu = ToggleAttachmentDropdown
 | 
			
		||||
                    , menuOpen = model.attachmentDropdownOpen
 | 
			
		||||
                    , items =
 | 
			
		||||
                        [ { icon = "fa fa-download"
 | 
			
		||||
                          , label = "Download file"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ download attachName
 | 
			
		||||
                                , href fileUrl
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon = "fa fa-file"
 | 
			
		||||
                          , label = "Rename file"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ href "#"
 | 
			
		||||
                                , onClick (EditAttachNameStart attach.id)
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon = "fa fa-file-archive"
 | 
			
		||||
                          , label = "Download original archive"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ href (fileUrl ++ "/archive")
 | 
			
		||||
                                , target "_new"
 | 
			
		||||
                                , classList [ ( "hidden", not hasArchive ) ]
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon = "fa fa-external-link-alt"
 | 
			
		||||
                          , label = "Original file"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ href (fileUrl ++ "/original")
 | 
			
		||||
                                , target "_new"
 | 
			
		||||
                                , classList [ ( "hidden", not attach.converted ) ]
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon =
 | 
			
		||||
                                if Maybe.withDefault settings.nativePdfPreview model.pdfNativeView then
 | 
			
		||||
                                    "fa fa-toggle-on"
 | 
			
		||||
 | 
			
		||||
                                else
 | 
			
		||||
                                    "fa fa-toggle-off"
 | 
			
		||||
                          , label = "Render pdf by browser"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ onClick (TogglePdfNativeView settings.nativePdfPreview)
 | 
			
		||||
                                , href "#"
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon =
 | 
			
		||||
                                if isAttachMetaOpen model attach.id then
 | 
			
		||||
                                    "fa fa-toggle-on"
 | 
			
		||||
 | 
			
		||||
                                else
 | 
			
		||||
                                    "fa fa-toggle-off"
 | 
			
		||||
                          , label = "View extracted data"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ onClick (AttachMetaClick attach.id)
 | 
			
		||||
                                , href "#"
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        , { icon = "fa fa-trash"
 | 
			
		||||
                          , label = "Delete this file"
 | 
			
		||||
                          , attrs =
 | 
			
		||||
                                [ onClick (RequestDeleteAttachment attach.id)
 | 
			
		||||
                                , href "#"
 | 
			
		||||
                                ]
 | 
			
		||||
                          }
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
attachmentVisible : Model -> Int -> Bool
 | 
			
		||||
attachmentVisible model pos =
 | 
			
		||||
    not model.sentMailsOpen
 | 
			
		||||
        && (if model.visibleAttach >= List.length model.item.attachments then
 | 
			
		||||
                pos == 0
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                model.visibleAttach == pos
 | 
			
		||||
           )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isAttachMetaOpen : Model -> String -> Bool
 | 
			
		||||
isAttachMetaOpen model id =
 | 
			
		||||
    model.attachMetaOpen && (Dict.get id model.attachMeta /= Nothing)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
editAttachmentName : Model -> Attachment -> Html Msg
 | 
			
		||||
editAttachmentName model attach =
 | 
			
		||||
    let
 | 
			
		||||
        am =
 | 
			
		||||
            Util.Maybe.filter (\m -> m.id == attach.id) model.attachRename
 | 
			
		||||
    in
 | 
			
		||||
    case am of
 | 
			
		||||
        Just m ->
 | 
			
		||||
            div [ class "flex flex-row border-l border-r px-2 py-2 dark:border-bluegray-600" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , value m.newName
 | 
			
		||||
                    , onInput EditAttachNameSet
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    , class "mr-2"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , button
 | 
			
		||||
                    [ class S.primaryButton
 | 
			
		||||
                    , onClick EditAttachNameSubmit
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-check" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , button
 | 
			
		||||
                    [ class S.secondaryButton
 | 
			
		||||
                    , onClick EditAttachNameCancel
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-times" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            span [ class "hidden" ] []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
attachmentSelect : Model -> Int -> Attachment -> Html Msg
 | 
			
		||||
attachmentSelect model _ _ =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-row border-l border-r px-2 py-2 dark:border-bluegray-600 "
 | 
			
		||||
        , class "overflow-x-auto overflow-y-none"
 | 
			
		||||
        , classList
 | 
			
		||||
            [ ( "hidden", not model.attachMenuOpen )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        (List.indexedMap (menuItem model) model.item.attachments)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
menuItem : Model -> Int -> Attachment -> Html Msg
 | 
			
		||||
menuItem model pos attach =
 | 
			
		||||
    let
 | 
			
		||||
        highlight =
 | 
			
		||||
            let
 | 
			
		||||
                dropId =
 | 
			
		||||
                    DD.getDropId model.attachDD
 | 
			
		||||
 | 
			
		||||
                dragId =
 | 
			
		||||
                    DD.getDragId model.attachDD
 | 
			
		||||
 | 
			
		||||
                enable =
 | 
			
		||||
                    Just attach.id == dropId && dropId /= dragId
 | 
			
		||||
            in
 | 
			
		||||
            [ ( "bg-gray-300 dark:bg-bluegray-700 current-drop-target", enable )
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        active =
 | 
			
		||||
            model.visibleAttach == pos
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        ([ classList <|
 | 
			
		||||
            [ ( "border-blue-500 dark:border-lightblue-500", pos == 0 )
 | 
			
		||||
            , ( "dark:border-bluegray-600", pos /= 0 )
 | 
			
		||||
            ]
 | 
			
		||||
                ++ highlight
 | 
			
		||||
         , class "block flex-col relative border rounded px-1 py-1 mr-2"
 | 
			
		||||
         , class " hover:shadow dark:hover:border-bluegray-500"
 | 
			
		||||
         , href "#"
 | 
			
		||||
         , onClick (SetActiveAttachment pos)
 | 
			
		||||
         ]
 | 
			
		||||
            ++ DD.draggable AttachDDMsg attach.id
 | 
			
		||||
            ++ DD.droppable AttachDDMsg attach.id
 | 
			
		||||
        )
 | 
			
		||||
        [ div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", not active )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "absolute right-1 top-1 text-blue-400 dark:text-lightblue-400 text-xl"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-check-circle ml-1" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ img
 | 
			
		||||
                [ src (Api.attachmentPreviewURL attach.id)
 | 
			
		||||
                , class "block w-20 mx-auto"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mt-1 text-sm break-all w-28 text-center" ]
 | 
			
		||||
            [ Maybe.map (Util.String.ellipsis 36) attach.name
 | 
			
		||||
                |> Maybe.withDefault "No Name"
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
@@ -22,6 +22,7 @@ import Comp.DetailEdit
 | 
			
		||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
 | 
			
		||||
import Comp.Dropzone
 | 
			
		||||
import Comp.EquipmentForm
 | 
			
		||||
import Comp.ItemDetail.EditForm
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( AttachmentRename
 | 
			
		||||
@@ -863,7 +864,10 @@ update key flags inav settings msg model =
 | 
			
		||||
            case Dict.get id model.attachMeta of
 | 
			
		||||
                Just _ ->
 | 
			
		||||
                    resultModel
 | 
			
		||||
                        { model | attachMetaOpen = not model.attachMetaOpen }
 | 
			
		||||
                        { model
 | 
			
		||||
                            | attachMetaOpen = not model.attachMetaOpen
 | 
			
		||||
                            , attachmentDropdownOpen = False
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    let
 | 
			
		||||
@@ -874,7 +878,11 @@ update key flags inav settings msg model =
 | 
			
		||||
                            Dict.insert id am model.attachMeta
 | 
			
		||||
                    in
 | 
			
		||||
                    resultModelCmd
 | 
			
		||||
                        ( { model | attachMeta = nextMeta, attachMetaOpen = True }
 | 
			
		||||
                        ( { model
 | 
			
		||||
                            | attachMeta = nextMeta
 | 
			
		||||
                            , attachMetaOpen = True
 | 
			
		||||
                            , attachmentDropdownOpen = False
 | 
			
		||||
                          }
 | 
			
		||||
                        , Cmd.map (AttachMetaMsg id) ac
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
@@ -901,6 +909,7 @@ update key flags inav settings msg model =
 | 
			
		||||
 | 
			
		||||
                            Nothing ->
 | 
			
		||||
                                Just (not default)
 | 
			
		||||
                    , attachmentDropdownOpen = False
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        DeleteAttachConfirm attachId lmsg ->
 | 
			
		||||
@@ -933,7 +942,7 @@ update key flags inav settings msg model =
 | 
			
		||||
                inav
 | 
			
		||||
                settings
 | 
			
		||||
                (DeleteAttachConfirm id Comp.YesNoDimmer.activate)
 | 
			
		||||
                model
 | 
			
		||||
                { model | attachmentDropdownOpen = False }
 | 
			
		||||
 | 
			
		||||
        AddFilesToggle ->
 | 
			
		||||
            resultModel
 | 
			
		||||
@@ -964,7 +973,7 @@ update key flags inav settings msg model =
 | 
			
		||||
            resultModel
 | 
			
		||||
                { model
 | 
			
		||||
                    | selectedFiles = []
 | 
			
		||||
                    , addFilesModel = Comp.Dropzone.init Comp.Dropzone.defaultSettings
 | 
			
		||||
                    , addFilesModel = Comp.Dropzone.init []
 | 
			
		||||
                    , completed = Set.empty
 | 
			
		||||
                    , errored = Set.empty
 | 
			
		||||
                    , loading = Dict.empty
 | 
			
		||||
@@ -1220,13 +1229,21 @@ update key flags inav settings msg model =
 | 
			
		||||
                    in
 | 
			
		||||
                    case name of
 | 
			
		||||
                        Just n ->
 | 
			
		||||
                            resultModel { model | attachRename = Just (AttachmentRename id n) }
 | 
			
		||||
                            resultModel
 | 
			
		||||
                                { model
 | 
			
		||||
                                    | attachRename = Just (AttachmentRename id n)
 | 
			
		||||
                                    , attachmentDropdownOpen = False
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                        Nothing ->
 | 
			
		||||
                            resultModel model
 | 
			
		||||
 | 
			
		||||
                Just _ ->
 | 
			
		||||
                    resultModel { model | attachRename = Nothing }
 | 
			
		||||
                    resultModel
 | 
			
		||||
                        { model
 | 
			
		||||
                            | attachRename = Nothing
 | 
			
		||||
                            , attachmentDropdownOpen = False
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
        EditAttachNameCancel ->
 | 
			
		||||
            resultModel { model | attachRename = Nothing }
 | 
			
		||||
@@ -1370,7 +1387,7 @@ update key flags inav settings msg model =
 | 
			
		||||
        CustomFieldMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                result =
 | 
			
		||||
                    Comp.CustomFieldMultiInput.update lm model.customFieldsModel
 | 
			
		||||
                    Comp.CustomFieldMultiInput.update flags lm model.customFieldsModel
 | 
			
		||||
 | 
			
		||||
                cmd_ =
 | 
			
		||||
                    Cmd.map CustomFieldMsg result.cmd
 | 
			
		||||
@@ -1460,6 +1477,36 @@ update key flags inav settings msg model =
 | 
			
		||||
        CustomFieldRemoveResp fieldId (Err _) ->
 | 
			
		||||
            resultModel { model | customFieldSavingIcon = Dict.remove fieldId model.customFieldSavingIcon }
 | 
			
		||||
 | 
			
		||||
        ToggleAttachmentDropdown ->
 | 
			
		||||
            resultModel { model | attachmentDropdownOpen = not model.attachmentDropdownOpen }
 | 
			
		||||
 | 
			
		||||
        ToggleAkkordionTab title ->
 | 
			
		||||
            let
 | 
			
		||||
                tabs =
 | 
			
		||||
                    if Set.member title model.editMenuTabsOpen then
 | 
			
		||||
                        Set.remove title model.editMenuTabsOpen
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Set.insert title model.editMenuTabsOpen
 | 
			
		||||
            in
 | 
			
		||||
            resultModel { model | editMenuTabsOpen = tabs }
 | 
			
		||||
 | 
			
		||||
        ToggleOpenAllAkkordionTabs ->
 | 
			
		||||
            let
 | 
			
		||||
                allNames =
 | 
			
		||||
                    Comp.ItemDetail.EditForm.formTabs settings model
 | 
			
		||||
                        |> List.map .title
 | 
			
		||||
                        |> Set.fromList
 | 
			
		||||
 | 
			
		||||
                next =
 | 
			
		||||
                    if model.editMenuTabsOpen == allNames then
 | 
			
		||||
                        Set.empty
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        allNames
 | 
			
		||||
            in
 | 
			
		||||
            resultModel { model | editMenuTabsOpen = next }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helper
 | 
			
		||||
 
 | 
			
		||||
@@ -525,7 +525,7 @@ renderItemInfo settings model =
 | 
			
		||||
                , title "Correspondent"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.correspondentIcon ""
 | 
			
		||||
                    :: Comp.LinkTarget.makeCorrLink model.item SetLinkTarget
 | 
			
		||||
                    :: Comp.LinkTarget.makeCorrLink model.item [] SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.CorrOrg
 | 
			
		||||
                || Data.UiSettings.fieldVisible settings Data.Fields.CorrPerson
 | 
			
		||||
@@ -537,7 +537,7 @@ renderItemInfo settings model =
 | 
			
		||||
                , title "Concerning"
 | 
			
		||||
                ]
 | 
			
		||||
                (Icons.concernedIcon
 | 
			
		||||
                    :: Comp.LinkTarget.makeConcLink model.item SetLinkTarget
 | 
			
		||||
                    :: Comp.LinkTarget.makeConcLink model.item [] SetLinkTarget
 | 
			
		||||
                )
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.ConcEquip
 | 
			
		||||
                || Data.UiSettings.fieldVisible settings Data.Fields.ConcPerson
 | 
			
		||||
@@ -549,7 +549,7 @@ renderItemInfo settings model =
 | 
			
		||||
                , title "Folder"
 | 
			
		||||
                ]
 | 
			
		||||
                [ Icons.folderIcon ""
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink model.item SetLinkTarget
 | 
			
		||||
                , Comp.LinkTarget.makeFolderLink model.item [] SetLinkTarget
 | 
			
		||||
                ]
 | 
			
		||||
            , Data.UiSettings.fieldVisible settings Data.Fields.Folder
 | 
			
		||||
            )
 | 
			
		||||
@@ -1093,7 +1093,11 @@ renderAddFilesForm model =
 | 
			
		||||
        [ h4 [ class "ui header" ]
 | 
			
		||||
            [ text "Add more files to this item"
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map AddFilesMsg (Comp.Dropzone.view model.addFilesModel)
 | 
			
		||||
        , Html.map AddFilesMsg
 | 
			
		||||
            (Comp.Dropzone.view
 | 
			
		||||
                Comp.Dropzone.defaultSettings
 | 
			
		||||
                model.addFilesModel
 | 
			
		||||
            )
 | 
			
		||||
        , button
 | 
			
		||||
            [ class "ui primary button"
 | 
			
		||||
            , href "#"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										289
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								modules/webapp/src/main/elm/Comp/ItemDetail/View2.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
module Comp.ItemDetail.View2 exposing (view)
 | 
			
		||||
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.DetailEdit
 | 
			
		||||
import Comp.ItemDetail.AddFilesForm
 | 
			
		||||
import Comp.ItemDetail.ItemInfoHeader
 | 
			
		||||
import Comp.ItemDetail.Model
 | 
			
		||||
    exposing
 | 
			
		||||
        ( Model
 | 
			
		||||
        , Msg(..)
 | 
			
		||||
        , NotesField(..)
 | 
			
		||||
        , SaveNameState(..)
 | 
			
		||||
        )
 | 
			
		||||
import Comp.ItemDetail.Notes
 | 
			
		||||
import Comp.ItemDetail.SingleAttachment
 | 
			
		||||
import Comp.ItemMail
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.SentMails
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
import Data.ItemNav exposing (ItemNav)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Page exposing (Page(..))
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : ItemNav -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view inav settings model =
 | 
			
		||||
    div [ class "flex flex-col h-full" ]
 | 
			
		||||
        [ header settings model
 | 
			
		||||
        , menuBar inav settings model
 | 
			
		||||
        , body inav settings model
 | 
			
		||||
        , Html.map DeleteItemConfirm
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                (Comp.YesNoDimmer.defaultSettings2 "Really delete the complete item?")
 | 
			
		||||
                model.deleteItemConfirm
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
header : UiSettings -> Model -> Html Msg
 | 
			
		||||
header settings model =
 | 
			
		||||
    div [ class "my-3" ]
 | 
			
		||||
        [ Comp.ItemDetail.ItemInfoHeader.view settings model ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
menuBar : ItemNav -> UiSettings -> Model -> Html Msg
 | 
			
		||||
menuBar inav settings model =
 | 
			
		||||
    let
 | 
			
		||||
        keyDescr name =
 | 
			
		||||
            if settings.itemDetailShortcuts && model.menuOpen then
 | 
			
		||||
                " Key '" ++ name ++ "'."
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                ""
 | 
			
		||||
    in
 | 
			
		||||
    MB.view
 | 
			
		||||
        { start =
 | 
			
		||||
            [ MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ class S.secondaryBasicButton
 | 
			
		||||
                    , Page.href HomePage
 | 
			
		||||
                    , title "Back to search results"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-arrow-left" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
            , MB.CustomElement <|
 | 
			
		||||
                div [ class "inline-flex" ]
 | 
			
		||||
                    [ B.genericButton
 | 
			
		||||
                        { label = ""
 | 
			
		||||
                        , icon = "fa fa-caret-left"
 | 
			
		||||
                        , baseStyle = S.secondaryBasicButtonMain ++ " px-4 py-2 border rounded-l"
 | 
			
		||||
                        , activeStyle = S.secondaryBasicButtonHover
 | 
			
		||||
                        , handler =
 | 
			
		||||
                            Maybe.map ItemDetailPage inav.prev
 | 
			
		||||
                                |> Maybe.map Page.href
 | 
			
		||||
                                |> Maybe.withDefault (href "#")
 | 
			
		||||
                        , disabled = inav.prev == Nothing
 | 
			
		||||
                        , attrs =
 | 
			
		||||
                            [ title ("Previous item." ++ keyDescr "Ctrl-,")
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    , B.genericButton
 | 
			
		||||
                        { label = ""
 | 
			
		||||
                        , icon = "fa fa-caret-right"
 | 
			
		||||
                        , baseStyle =
 | 
			
		||||
                            S.secondaryBasicButtonMain
 | 
			
		||||
                                ++ " px-4 py-2 border-t border-b border-r rounded-r"
 | 
			
		||||
                        , activeStyle = S.secondaryBasicButtonHover
 | 
			
		||||
                        , handler =
 | 
			
		||||
                            Maybe.map ItemDetailPage inav.next
 | 
			
		||||
                                |> Maybe.map Page.href
 | 
			
		||||
                                |> Maybe.withDefault (href "#")
 | 
			
		||||
                        , disabled = inav.next == Nothing
 | 
			
		||||
                        , attrs =
 | 
			
		||||
                            [ title ("Next item." ++ keyDescr "Ctrl-.")
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
            , MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( "bg-gray-200 dark:bg-bluegray-600", model.mailOpen )
 | 
			
		||||
                        ]
 | 
			
		||||
                    , title "Send Mail"
 | 
			
		||||
                    , onClick ToggleMail
 | 
			
		||||
                    , class S.secondaryBasicButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-envelope font-thin" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
            , MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ classList
 | 
			
		||||
                        [ ( "bg-gray-200 dark:bg-bluegray-600", model.addFilesOpen )
 | 
			
		||||
                        ]
 | 
			
		||||
                    , if model.addFilesOpen then
 | 
			
		||||
                        title "Close"
 | 
			
		||||
 | 
			
		||||
                      else
 | 
			
		||||
                        title "Add more files to this item"
 | 
			
		||||
                    , onClick AddFilesToggle
 | 
			
		||||
                    , class S.secondaryBasicButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ Icons.addFilesIcon2 ""
 | 
			
		||||
                    ]
 | 
			
		||||
            , MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ class S.primaryButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , onClick ConfirmItem
 | 
			
		||||
                    , title "Confirm item metadata"
 | 
			
		||||
                    , classList [ ( "hidden", model.item.state /= "created" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-check mr-2" ] []
 | 
			
		||||
                    , text "Confirm"
 | 
			
		||||
                    ]
 | 
			
		||||
            ]
 | 
			
		||||
        , end =
 | 
			
		||||
            [ MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ class S.secondaryBasicButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , onClick UnconfirmItem
 | 
			
		||||
                    , title "Un-confirm item metadata"
 | 
			
		||||
                    , classList [ ( "hidden", model.item.state == "created" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-eye-slash font-thin" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
            , MB.CustomElement <|
 | 
			
		||||
                a
 | 
			
		||||
                    [ class S.deleteButton
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , onClick RequestDelete
 | 
			
		||||
                    , title "Delete this item"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-trash" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
            ]
 | 
			
		||||
        , rootClasses = "mb-2"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
body : ItemNav -> UiSettings -> Model -> Html Msg
 | 
			
		||||
body inav settings model =
 | 
			
		||||
    div [ class "grid gap-2 grid-cols-1 md:grid-cols-3 h-full" ]
 | 
			
		||||
        [ leftArea settings model
 | 
			
		||||
        , rightArea settings model
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
leftArea : UiSettings -> Model -> Html Msg
 | 
			
		||||
leftArea settings model =
 | 
			
		||||
    div [ class "w-full md:order-first md:mr-2 flex flex-col" ]
 | 
			
		||||
        [ addDetailForm settings model
 | 
			
		||||
        , sendMailForm settings model
 | 
			
		||||
        , Comp.ItemDetail.AddFilesForm.view model
 | 
			
		||||
        , Comp.ItemDetail.Notes.view model
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Comp.SentMails.isEmpty model.sentMails )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "mt-4 "
 | 
			
		||||
            ]
 | 
			
		||||
            [ h3 [ class "flex flex-row items-center border-b dark:border-bluegray-600 font-bold text-lg" ]
 | 
			
		||||
                [ text "Sent E-Mails"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map SentMailsMsg (Comp.SentMails.view2 model.sentMails)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , itemIdInfo model
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
rightArea : UiSettings -> Model -> Html Msg
 | 
			
		||||
rightArea settings model =
 | 
			
		||||
    div [ class "md:col-span-2 h-full" ]
 | 
			
		||||
        (attachmentsBody settings model)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
attachmentsBody : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
attachmentsBody settings model =
 | 
			
		||||
    List.indexedMap (Comp.ItemDetail.SingleAttachment.view settings model)
 | 
			
		||||
        model.item.attachments
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sendMailForm : UiSettings -> Model -> Html Msg
 | 
			
		||||
sendMailForm settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "hidden", not model.mailOpen )
 | 
			
		||||
            ]
 | 
			
		||||
        , class S.box
 | 
			
		||||
        , class "mb-4 px-2 py-2"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "text-lg font-bold" ]
 | 
			
		||||
            [ text "Send this item via E-Mail"
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.mailSending
 | 
			
		||||
        , Html.map ItemMailMsg (Comp.ItemMail.view2 settings model.itemMail)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.errorMessage
 | 
			
		||||
                  , Maybe.map .success model.mailSendResult
 | 
			
		||||
                        |> Maybe.map not
 | 
			
		||||
                        |> Maybe.withDefault False
 | 
			
		||||
                  )
 | 
			
		||||
                , ( S.successMessage
 | 
			
		||||
                  , Maybe.map .success model.mailSendResult
 | 
			
		||||
                        |> Maybe.withDefault False
 | 
			
		||||
                  )
 | 
			
		||||
                , ( "hidden", model.mailSendResult == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "mt-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.mailSendResult
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
itemIdInfo : Model -> Html msg
 | 
			
		||||
itemIdInfo model =
 | 
			
		||||
    div [ class "flex flex-col opacity-50 text-xs pb-1 mt-3 border-t dark:border-bluegray-600" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "inline-flex items-center"
 | 
			
		||||
            , title "Item ID"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-bullseye mr-2" ] []
 | 
			
		||||
            , text model.item.id
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "inline-flex items-center"
 | 
			
		||||
            , title "Created on"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-sun font-thin mr-2" ] []
 | 
			
		||||
            , Util.Time.formatDateTime model.item.created |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "inline-flex items-center"
 | 
			
		||||
            , title "Last update on"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-pencil-alt mr-2" ] []
 | 
			
		||||
            , Util.Time.formatDateTime model.item.updated |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
addDetailForm : UiSettings -> Model -> Html Msg
 | 
			
		||||
addDetailForm settings model =
 | 
			
		||||
    case model.modalEdit of
 | 
			
		||||
        Just mm ->
 | 
			
		||||
            div
 | 
			
		||||
                [ class "flex flex-col px-2 py-2 mb-4"
 | 
			
		||||
                , class S.box
 | 
			
		||||
                ]
 | 
			
		||||
                [ Comp.DetailEdit.formHeading S.header3 mm
 | 
			
		||||
                , Html.map ModalEditMsg (Comp.DetailEdit.view2 [] settings mm)
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            span [ class "hidden" ] []
 | 
			
		||||
@@ -7,19 +7,24 @@ module Comp.ItemMail exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
 | 
			
		||||
import Api.Model.SimpleMail exposing (SimpleMail)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.EmailInput
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Data.DropdownStyle
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -216,6 +221,10 @@ isValid model =
 | 
			
		||||
        == Nothing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -293,3 +302,104 @@ view settings model =
 | 
			
		||||
            [ text "Cancel"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        dds =
 | 
			
		||||
            Data.DropdownStyle.mainStyle
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col"
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Send via"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map ConnMsg (Comp.Dropdown.view2 dds settings model.connectionModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.errorMessage
 | 
			
		||||
            , classList [ ( "hidden", model.formError == Nothing ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Recipient(s)"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map RecipientMsg
 | 
			
		||||
                (Comp.EmailInput.view2 dds model.recipients model.recipientsModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "CC(s)"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map CCRecipientMsg
 | 
			
		||||
                (Comp.EmailInput.view2 dds model.ccRecipients model.ccRecipientsModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "BCC(s)"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map BCCRecipientMsg
 | 
			
		||||
                (Comp.EmailInput.view2 dds model.bccRecipients model.bccRecipientsModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Subject"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , onInput SetSubject
 | 
			
		||||
                , value model.subject
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Body"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , textarea
 | 
			
		||||
                [ onInput SetBody
 | 
			
		||||
                , value model.body
 | 
			
		||||
                , class S.textAreaInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , MB.viewItem <|
 | 
			
		||||
            MB.Checkbox
 | 
			
		||||
                { tagger = \_ -> ToggleAttachAll
 | 
			
		||||
                , label = "Include all item attachments"
 | 
			
		||||
                , value = model.attachAll
 | 
			
		||||
                , id = "item-send-mail-attach-all"
 | 
			
		||||
                }
 | 
			
		||||
        , div [ class "flex flex-row space-x-2" ]
 | 
			
		||||
            [ B.primaryButton
 | 
			
		||||
                { label = "Send"
 | 
			
		||||
                , icon = "fa fa-paper-plane font-thin"
 | 
			
		||||
                , handler = onClick Send
 | 
			
		||||
                , attrs = [ href "#" ]
 | 
			
		||||
                , disabled = not (isValid model)
 | 
			
		||||
                }
 | 
			
		||||
            , B.secondaryButton
 | 
			
		||||
                { label = "Cancel"
 | 
			
		||||
                , icon = "fa fa-times"
 | 
			
		||||
                , handler = onClick Cancel
 | 
			
		||||
                , attrs = [ href "#" ]
 | 
			
		||||
                , disabled = False
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,16 @@ module Comp.LinkTarget exposing
 | 
			
		||||
    , makeConcLink
 | 
			
		||||
    , makeCorrLink
 | 
			
		||||
    , makeCustomFieldLink
 | 
			
		||||
    , makeCustomFieldLink2
 | 
			
		||||
    , makeFolderLink
 | 
			
		||||
    , makeSourceLink
 | 
			
		||||
    , makeTagIconLink
 | 
			
		||||
    , makeTagLink
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
@@ -30,42 +33,45 @@ type LinkTarget
 | 
			
		||||
 | 
			
		||||
makeCorrLink :
 | 
			
		||||
    { a | corrOrg : Maybe IdName, corrPerson : Maybe IdName }
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> List (Html msg)
 | 
			
		||||
makeCorrLink item tagger =
 | 
			
		||||
makeCorrLink item linkClasses tagger =
 | 
			
		||||
    let
 | 
			
		||||
        makeOrg idname =
 | 
			
		||||
            makeLink [] (LinkCorrOrg >> tagger) idname
 | 
			
		||||
            makeLink linkClasses (LinkCorrOrg >> tagger) idname
 | 
			
		||||
 | 
			
		||||
        makePerson idname =
 | 
			
		||||
            makeLink [] (LinkCorrPerson >> tagger) idname
 | 
			
		||||
            makeLink linkClasses (LinkCorrPerson >> tagger) idname
 | 
			
		||||
    in
 | 
			
		||||
    combine (Maybe.map makeOrg item.corrOrg) (Maybe.map makePerson item.corrPerson)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeConcLink :
 | 
			
		||||
    { a | concPerson : Maybe IdName, concEquipment : Maybe IdName }
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> List (Html msg)
 | 
			
		||||
makeConcLink item tagger =
 | 
			
		||||
makeConcLink item linkClasses tagger =
 | 
			
		||||
    let
 | 
			
		||||
        makePerson idname =
 | 
			
		||||
            makeLink [] (LinkConcPerson >> tagger) idname
 | 
			
		||||
            makeLink linkClasses (LinkConcPerson >> tagger) idname
 | 
			
		||||
 | 
			
		||||
        makeEquip idname =
 | 
			
		||||
            makeLink [] (LinkConcEquip >> tagger) idname
 | 
			
		||||
            makeLink linkClasses (LinkConcEquip >> tagger) idname
 | 
			
		||||
    in
 | 
			
		||||
    combine (Maybe.map makePerson item.concPerson) (Maybe.map makeEquip item.concEquipment)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeFolderLink :
 | 
			
		||||
    { a | folder : Maybe IdName }
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeFolderLink item tagger =
 | 
			
		||||
makeFolderLink item linkClasses tagger =
 | 
			
		||||
    let
 | 
			
		||||
        makeFolder idname =
 | 
			
		||||
            makeLink [] (LinkFolder >> tagger) idname
 | 
			
		||||
            makeLink linkClasses (LinkFolder >> tagger) idname
 | 
			
		||||
    in
 | 
			
		||||
    Maybe.map makeFolder item.folder
 | 
			
		||||
        |> Maybe.withDefault (text "-")
 | 
			
		||||
@@ -77,7 +83,17 @@ makeTagLink :
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeTagLink tagId classes tagger =
 | 
			
		||||
    makeLink classes (LinkTag >> tagger) tagId
 | 
			
		||||
    makeIconLink (i [ class "fa fa-tag mr-2" ] []) classes (LinkTag >> tagger) tagId
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeTagIconLink :
 | 
			
		||||
    Tag
 | 
			
		||||
    -> Html msg
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeTagIconLink tagId icon classes tagger =
 | 
			
		||||
    makeIconLink icon classes (LinkTag >> tagger) tagId
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeCustomFieldLink :
 | 
			
		||||
@@ -92,6 +108,18 @@ makeCustomFieldLink cv classes tagger =
 | 
			
		||||
        cv
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeCustomFieldLink2 :
 | 
			
		||||
    ItemFieldValue
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeCustomFieldLink2 cv classes tagger =
 | 
			
		||||
    Util.CustomField.renderValue2
 | 
			
		||||
        classes
 | 
			
		||||
        (tagger (LinkCustomField cv) |> Just)
 | 
			
		||||
        cv
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeSourceLink :
 | 
			
		||||
    List ( String, Bool )
 | 
			
		||||
    -> (LinkTarget -> msg)
 | 
			
		||||
@@ -130,3 +158,22 @@ makeLink classes tagger idname =
 | 
			
		||||
        ]
 | 
			
		||||
        [ text idname.name
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeIconLink :
 | 
			
		||||
    Html msg
 | 
			
		||||
    -> List ( String, Bool )
 | 
			
		||||
    -> (IdName -> msg)
 | 
			
		||||
    -> { x | id : String, name : String }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeIconLink icon classes tagger tag =
 | 
			
		||||
    a
 | 
			
		||||
        [ onClick (tagger (IdName tag.id tag.name))
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , classList classes
 | 
			
		||||
        ]
 | 
			
		||||
        [ icon
 | 
			
		||||
        , span []
 | 
			
		||||
            [ text tag.name
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,16 +5,22 @@ module Comp.MarkdownInput exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , viewCheatLink
 | 
			
		||||
    , viewCheatLink2
 | 
			
		||||
    , viewContent
 | 
			
		||||
    , viewContent2
 | 
			
		||||
    , viewEditLink
 | 
			
		||||
    , viewEditLink2
 | 
			
		||||
    , viewPreviewLink
 | 
			
		||||
    , viewPreviewLink2
 | 
			
		||||
    , viewSplitLink
 | 
			
		||||
    , viewSplitLink2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Markdown
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Display
 | 
			
		||||
@@ -51,6 +57,10 @@ update txt msg model =
 | 
			
		||||
            ( { model | display = dsp }, txt )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewContent : String -> Model -> Html Msg
 | 
			
		||||
viewContent txt model =
 | 
			
		||||
    case model.display of
 | 
			
		||||
@@ -172,3 +182,94 @@ splitDisplay txt =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewContent2 : String -> Model -> Html Msg
 | 
			
		||||
viewContent2 txt model =
 | 
			
		||||
    case model.display of
 | 
			
		||||
        Edit ->
 | 
			
		||||
            editDisplay2 txt
 | 
			
		||||
 | 
			
		||||
        Preview ->
 | 
			
		||||
            previewDisplay2 txt
 | 
			
		||||
 | 
			
		||||
        Split ->
 | 
			
		||||
            splitDisplay2 txt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewEditLink2 : (Bool -> Attribute Msg) -> Model -> Html Msg
 | 
			
		||||
viewEditLink2 classes model =
 | 
			
		||||
    a
 | 
			
		||||
        [ onClick (SetDisplay Edit)
 | 
			
		||||
        , classes (model.display == Edit)
 | 
			
		||||
        , href "#"
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Edit"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewPreviewLink2 : (Bool -> Attribute Msg) -> Model -> Html Msg
 | 
			
		||||
viewPreviewLink2 classes model =
 | 
			
		||||
    a
 | 
			
		||||
        [ onClick (SetDisplay Preview)
 | 
			
		||||
        , classes (model.display == Preview)
 | 
			
		||||
        , href "#"
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Preview"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewSplitLink2 : (Bool -> Attribute Msg) -> Model -> Html Msg
 | 
			
		||||
viewSplitLink2 classes model =
 | 
			
		||||
    a
 | 
			
		||||
        [ onClick (SetDisplay Split)
 | 
			
		||||
        , classes (model.display == Split)
 | 
			
		||||
        , href "#"
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Split"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCheatLink2 : String -> Model -> Html msg
 | 
			
		||||
viewCheatLink2 classes model =
 | 
			
		||||
    a
 | 
			
		||||
        [ class classes
 | 
			
		||||
        , target "_new"
 | 
			
		||||
        , href model.cheatSheetUrl
 | 
			
		||||
        ]
 | 
			
		||||
        [ i [ class "fa fa-question mr-2" ] []
 | 
			
		||||
        , text "Supports Markdown"
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
editDisplay2 : String -> Html Msg
 | 
			
		||||
editDisplay2 txt =
 | 
			
		||||
    textarea
 | 
			
		||||
        [ class S.textAreaInput
 | 
			
		||||
        , class "h-full"
 | 
			
		||||
        , onInput SetText
 | 
			
		||||
        , placeholder "Add notes here…"
 | 
			
		||||
        , value txt
 | 
			
		||||
        ]
 | 
			
		||||
        []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
previewDisplay2 : String -> Html Msg
 | 
			
		||||
previewDisplay2 txt =
 | 
			
		||||
    Markdown.toHtml [ class "markdown-preview" ] txt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
splitDisplay2 : String -> Html Msg
 | 
			
		||||
splitDisplay2 txt =
 | 
			
		||||
    div [ class "flex flex-row justify-evenly" ]
 | 
			
		||||
        [ div [ class "w-1/2" ]
 | 
			
		||||
            [ editDisplay2 txt
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "w-1/2" ]
 | 
			
		||||
            [ previewDisplay2 txt
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										311
									
								
								modules/webapp/src/main/elm/Comp/MenuBar.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								modules/webapp/src/main/elm/Comp/MenuBar.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,311 @@
 | 
			
		||||
module Comp.MenuBar exposing
 | 
			
		||||
    ( ButtonData
 | 
			
		||||
    , CheckboxData
 | 
			
		||||
    , Item(..)
 | 
			
		||||
    , MenuBar
 | 
			
		||||
    , TextInputData
 | 
			
		||||
    , view
 | 
			
		||||
    , viewItem
 | 
			
		||||
    , viewSide
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Data.DropdownStyle exposing (DropdownStyle)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onBlur, onCheck, onClick, onFocus, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Item msg
 | 
			
		||||
    = TextInput (TextInputData msg)
 | 
			
		||||
    | Checkbox (CheckboxData msg)
 | 
			
		||||
    | PrimaryButton (ButtonData msg)
 | 
			
		||||
    | SecondaryButton (ButtonData msg)
 | 
			
		||||
    | DeleteButton (ButtonData msg)
 | 
			
		||||
    | BasicButton (ButtonData msg)
 | 
			
		||||
    | CustomButton (CustomButtonData msg)
 | 
			
		||||
    | TextLabel LabelData
 | 
			
		||||
    | CustomElement (Html msg)
 | 
			
		||||
    | Dropdown (DropdownData msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias MenuBar msg =
 | 
			
		||||
    { start : List (Item msg)
 | 
			
		||||
    , end : List (Item msg)
 | 
			
		||||
    , rootClasses : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias TextInputData msg =
 | 
			
		||||
    { tagger : String -> msg
 | 
			
		||||
    , value : String
 | 
			
		||||
    , placeholder : String
 | 
			
		||||
    , icon : Maybe String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias CheckboxData msg =
 | 
			
		||||
    { tagger : Bool -> msg
 | 
			
		||||
    , label : String
 | 
			
		||||
    , value : Bool
 | 
			
		||||
    , id : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias ButtonData msg =
 | 
			
		||||
    { tagger : msg
 | 
			
		||||
    , title : String
 | 
			
		||||
    , icon : Maybe String
 | 
			
		||||
    , label : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias CustomButtonData msg =
 | 
			
		||||
    { tagger : msg
 | 
			
		||||
    , title : String
 | 
			
		||||
    , icon : Maybe String
 | 
			
		||||
    , label : String
 | 
			
		||||
    , inputClass : List ( String, Bool )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias LabelData =
 | 
			
		||||
    { icon : String
 | 
			
		||||
    , label : String
 | 
			
		||||
    , class : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias DropdownData msg =
 | 
			
		||||
    { linkIcon : String
 | 
			
		||||
    , linkClass : List ( String, Bool )
 | 
			
		||||
    , toggleMenu : msg
 | 
			
		||||
    , menuOpen : Bool
 | 
			
		||||
    , items : List (DropdownMenu msg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias DropdownMenu msg =
 | 
			
		||||
    { icon : String
 | 
			
		||||
    , label : String
 | 
			
		||||
    , attrs : List (Attribute msg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : MenuBar msg -> Html msg
 | 
			
		||||
view =
 | 
			
		||||
    view1 "bg-white dark:bg-bluegray-800"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewSide : MenuBar msg -> Html msg
 | 
			
		||||
viewSide =
 | 
			
		||||
    view1 "bg-blue-50 dark:bg-bluegray-700"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view1 : String -> MenuBar msg -> Html msg
 | 
			
		||||
view1 classes mb =
 | 
			
		||||
    let
 | 
			
		||||
        left =
 | 
			
		||||
            div [ class "flex flex-row items-center space-x-2 w-full" ]
 | 
			
		||||
                (List.map viewItem mb.start)
 | 
			
		||||
 | 
			
		||||
        right =
 | 
			
		||||
            div [ class "flex-grow flex-row flex justify-end space-x-2 w-full" ]
 | 
			
		||||
                (List.map viewItem mb.end)
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class mb.rootClasses
 | 
			
		||||
        , class "flex flex-col sm:flex-row space-y-1 sm:space-y-0 sticky top-0 z-40"
 | 
			
		||||
        , class classes
 | 
			
		||||
        ]
 | 
			
		||||
        [ left
 | 
			
		||||
        , right
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem : Item msg -> Html msg
 | 
			
		||||
viewItem item =
 | 
			
		||||
    case item of
 | 
			
		||||
        TextInput model ->
 | 
			
		||||
            makeInput model
 | 
			
		||||
 | 
			
		||||
        Checkbox model ->
 | 
			
		||||
            makeCheckbox model
 | 
			
		||||
 | 
			
		||||
        PrimaryButton model ->
 | 
			
		||||
            makeButton [ ( S.primaryButton, True ) ] model
 | 
			
		||||
 | 
			
		||||
        SecondaryButton model ->
 | 
			
		||||
            makeButton [ ( S.secondaryButton, True ) ] model
 | 
			
		||||
 | 
			
		||||
        DeleteButton model ->
 | 
			
		||||
            makeButton [ ( S.deleteButton, True ) ] model
 | 
			
		||||
 | 
			
		||||
        BasicButton model ->
 | 
			
		||||
            makeButton [ ( S.secondaryBasicButton, True ) ] model
 | 
			
		||||
 | 
			
		||||
        CustomButton model ->
 | 
			
		||||
            makeButton model.inputClass model
 | 
			
		||||
 | 
			
		||||
        TextLabel model ->
 | 
			
		||||
            makeLabel model
 | 
			
		||||
 | 
			
		||||
        CustomElement v ->
 | 
			
		||||
            v
 | 
			
		||||
 | 
			
		||||
        Dropdown model ->
 | 
			
		||||
            makeDropdown model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeDropdown : DropdownData msg -> Html msg
 | 
			
		||||
makeDropdown model =
 | 
			
		||||
    let
 | 
			
		||||
        menuStyle =
 | 
			
		||||
            "absolute right-0 bg-white dark:bg-bluegray-800 border dark:border-bluegray-700 z-50 dark:text-bluegray-300 shadow-lg transition duration-200 min-w-max  "
 | 
			
		||||
 | 
			
		||||
        itemStyle =
 | 
			
		||||
            "transition-colors duration-200 items-center block px-4 py-2 text-normal hover:bg-gray-200 dark:hover:bg-bluegray-700 dark:hover:text-bluegray-50"
 | 
			
		||||
 | 
			
		||||
        menuItem m =
 | 
			
		||||
            a
 | 
			
		||||
                (class itemStyle :: m.attrs)
 | 
			
		||||
                [ i
 | 
			
		||||
                    [ class m.icon
 | 
			
		||||
                    , classList [ ( "hidden", m.icon == "" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span
 | 
			
		||||
                    [ class "ml-2"
 | 
			
		||||
                    , classList [ ( "hidden", m.label == "" ) ]
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text m.label
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "relative" ]
 | 
			
		||||
        [ a
 | 
			
		||||
            [ classList model.linkClass
 | 
			
		||||
            , class "block"
 | 
			
		||||
            , href "#"
 | 
			
		||||
            , onClick model.toggleMenu
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class model.linkIcon ] []
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class menuStyle
 | 
			
		||||
            , classList [ ( "hidden", not model.menuOpen ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map menuItem model.items)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeLabel : LabelData -> Html msg
 | 
			
		||||
makeLabel model =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex items-center justify-center "
 | 
			
		||||
        , class model.class
 | 
			
		||||
        ]
 | 
			
		||||
        [ i
 | 
			
		||||
            [ class model.icon
 | 
			
		||||
            , classList [ ( "hidden", model.icon == "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , text model.label
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeButton :
 | 
			
		||||
    List ( String, Bool )
 | 
			
		||||
    ->
 | 
			
		||||
        { e
 | 
			
		||||
            | tagger : msg
 | 
			
		||||
            , title : String
 | 
			
		||||
            , icon : Maybe String
 | 
			
		||||
            , label : String
 | 
			
		||||
        }
 | 
			
		||||
    -> Html msg
 | 
			
		||||
makeButton btnType model =
 | 
			
		||||
    let
 | 
			
		||||
        ( icon, iconMargin ) =
 | 
			
		||||
            case model.icon of
 | 
			
		||||
                Just cls ->
 | 
			
		||||
                    ( [ i [ class cls ] []
 | 
			
		||||
                      ]
 | 
			
		||||
                    , if model.label == "" then
 | 
			
		||||
                        ""
 | 
			
		||||
 | 
			
		||||
                      else
 | 
			
		||||
                        "ml-2"
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ( [], "" )
 | 
			
		||||
 | 
			
		||||
        label =
 | 
			
		||||
            if model.label == "" then
 | 
			
		||||
                []
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                [ span [ class (iconMargin ++ " hidden sm:inline") ]
 | 
			
		||||
                    [ text model.label
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        [ classList btnType
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , onClick model.tagger
 | 
			
		||||
        , title model.title
 | 
			
		||||
        ]
 | 
			
		||||
        (icon ++ label)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeCheckbox : CheckboxData msg -> Html msg
 | 
			
		||||
makeCheckbox model =
 | 
			
		||||
    div [ class "" ]
 | 
			
		||||
        [ label
 | 
			
		||||
            [ class "inline-flex space-x-2 items-center"
 | 
			
		||||
            , for model.id
 | 
			
		||||
            ]
 | 
			
		||||
            [ input
 | 
			
		||||
                [ type_ "checkbox"
 | 
			
		||||
                , onCheck model.tagger
 | 
			
		||||
                , checked model.value
 | 
			
		||||
                , class S.checkboxInput
 | 
			
		||||
                , id model.id
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , span [ class "truncate" ]
 | 
			
		||||
                [ text model.label
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeInput : TextInputData msg -> Html msg
 | 
			
		||||
makeInput model =
 | 
			
		||||
    let
 | 
			
		||||
        ( icon, iconPad ) =
 | 
			
		||||
            case model.icon of
 | 
			
		||||
                Just cls ->
 | 
			
		||||
                    ( [ div [ class S.inputIcon ]
 | 
			
		||||
                            [ i [ class cls ] []
 | 
			
		||||
                            ]
 | 
			
		||||
                      ]
 | 
			
		||||
                    , "pl-10"
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ( [], "" )
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "relative pr-2" ]
 | 
			
		||||
        (input
 | 
			
		||||
            [ type_ "text"
 | 
			
		||||
            , onInput model.tagger
 | 
			
		||||
            , value model.value
 | 
			
		||||
            , placeholder model.placeholder
 | 
			
		||||
            , class (iconPad ++ " pr-4 py-1 rounded" ++ S.textInput)
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
            :: icon
 | 
			
		||||
        )
 | 
			
		||||
@@ -6,6 +6,7 @@ module Comp.NotificationForm exposing
 | 
			
		||||
    , initWith
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -14,12 +15,15 @@ import Api.Model.EmailSettingsList exposing (EmailSettingsList)
 | 
			
		||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Api.Model.TagList exposing (TagList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.CalEventInput
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Comp.EmailInput
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.CalEvent exposing (CalEvent)
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Data.Validated exposing (Validated(..))
 | 
			
		||||
@@ -27,6 +31,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Tag
 | 
			
		||||
@@ -142,8 +147,8 @@ init flags =
 | 
			
		||||
                { makeOption = \a -> { value = a, text = a, additional = "" }
 | 
			
		||||
                , placeholder = "Select connection..."
 | 
			
		||||
                }
 | 
			
		||||
      , tagInclModel = Util.Tag.makeDropdownModel
 | 
			
		||||
      , tagExclModel = Util.Tag.makeDropdownModel
 | 
			
		||||
      , tagInclModel = Util.Tag.makeDropdownModel2
 | 
			
		||||
      , tagExclModel = Util.Tag.makeDropdownModel2
 | 
			
		||||
      , recipients = []
 | 
			
		||||
      , recipientsModel = Comp.EmailInput.init
 | 
			
		||||
      , remindDays = Just 1
 | 
			
		||||
@@ -622,3 +627,185 @@ view extraClasses settings model =
 | 
			
		||||
            [ text "Start Once"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View 2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 extraClasses settings model =
 | 
			
		||||
    let
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this notification task?"
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col md:relative"
 | 
			
		||||
        , class extraClasses
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoDeleteMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.yesNoDelete
 | 
			
		||||
            )
 | 
			
		||||
        , B.loadingDimmer (model.loading > 0)
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    , title = "Save"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = Cancel
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if model.settings.id /= "" then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        , title = "Delete this task"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.successMessage, isFormSuccess model )
 | 
			
		||||
                , ( S.errorMessage, isFormError model )
 | 
			
		||||
                , ( "hidden", model.formMsg == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "mb-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.formMsg
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleEnabled
 | 
			
		||||
                    , label = "Enable or disable this task."
 | 
			
		||||
                    , value = model.enabled
 | 
			
		||||
                    , id = "notify-enabled"
 | 
			
		||||
                    }
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Send via"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map ConnMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.connectionModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "The SMTP connection to use when sending notification mails."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Recipient(s)"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map RecipientMsg
 | 
			
		||||
                (Comp.EmailInput.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    model.recipients
 | 
			
		||||
                    model.recipientsModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "One or more mail addresses, confirm each by pressing 'Return'."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Tags Include (and)" ]
 | 
			
		||||
            , Html.map TagIncMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.tagInclModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Items must have all the tags specified here."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Tags Exclude (or)" ]
 | 
			
		||||
            , Html.map TagExcMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.tagExclModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "small-info" ]
 | 
			
		||||
                [ text "Items must not have any tag specified here."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map RemindDaysMsg
 | 
			
		||||
            (Comp.IntField.viewWithInfo2
 | 
			
		||||
                "Select items with a due date *lower than* `today+remindDays`"
 | 
			
		||||
                model.remindDays
 | 
			
		||||
                "mb-4"
 | 
			
		||||
                model.remindDaysModel
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { tagger = \_ -> ToggleCapOverdue
 | 
			
		||||
                    , id = "notify-toggle-cap-overdue"
 | 
			
		||||
                    , value = model.capOverdue
 | 
			
		||||
                    , label = "Cap overdue items"
 | 
			
		||||
                    }
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "If checked, only items with a due date"
 | 
			
		||||
                , em [ class "font-italic" ]
 | 
			
		||||
                    [ text " greater than " ]
 | 
			
		||||
                , code [ class "font-mono" ]
 | 
			
		||||
                    [ text "today-remindDays" ]
 | 
			
		||||
                , text " are considered."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Schedule"
 | 
			
		||||
                , a
 | 
			
		||||
                    [ class "float-right"
 | 
			
		||||
                    , class S.link
 | 
			
		||||
                    , href "https://github.com/eikek/calev#what-are-calendar-events"
 | 
			
		||||
                    , target "_blank"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-question" ] []
 | 
			
		||||
                    , span [ class "pl-2" ]
 | 
			
		||||
                        [ text "Click here for help"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map CalEventMsg
 | 
			
		||||
                (Comp.CalEventInput.view2 ""
 | 
			
		||||
                    (Data.Validated.value model.schedule)
 | 
			
		||||
                    model.scheduleModel
 | 
			
		||||
                )
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Specify how often and when this task should run. "
 | 
			
		||||
                , text "Use English 3-letter weekdays. Either a single value, "
 | 
			
		||||
                , text "a list (ex. 1,2,3), a range (ex. 1..3) or a '*' (meaning all) "
 | 
			
		||||
                , text "is allowed for each part."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.NotificationList exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +42,10 @@ update msg model =
 | 
			
		||||
            ( model, EditAction settings )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List NotificationSettings -> Html Msg
 | 
			
		||||
view _ items =
 | 
			
		||||
    div []
 | 
			
		||||
@@ -93,3 +100,57 @@ viewItem item =
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> List NotificationSettings -> Html Msg
 | 
			
		||||
view2 _ items =
 | 
			
		||||
    div []
 | 
			
		||||
        [ table [ class S.tableMain ]
 | 
			
		||||
            [ thead []
 | 
			
		||||
                [ tr []
 | 
			
		||||
                    [ th [ class "" ] []
 | 
			
		||||
                    , th [ class "text-center mr-2" ]
 | 
			
		||||
                        [ i [ class "fa fa-check" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    , th [ class "text-left hidden sm:table-cell mr-2" ]
 | 
			
		||||
                        [ text "Schedule" ]
 | 
			
		||||
                    , th [ class "text-left mr-2" ]
 | 
			
		||||
                        [ text "Connection" ]
 | 
			
		||||
                    , th [ class "text-left hidden sm:table-cell mr-2" ]
 | 
			
		||||
                        [ text "Recipients" ]
 | 
			
		||||
                    , th [ class "text-center " ] [ text "Remind Days" ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , tbody []
 | 
			
		||||
                (List.map viewItem2 items)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : NotificationSettings -> Html Msg
 | 
			
		||||
viewItem2 item =
 | 
			
		||||
    tr []
 | 
			
		||||
        [ B.editLinkTableCell (EditSettings item)
 | 
			
		||||
        , td [ class "w-px whitespace-nowrap px-2 text-center" ]
 | 
			
		||||
            [ Util.Html.checkbox2 item.enabled
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left hidden sm:table-cell mr-2" ]
 | 
			
		||||
            [ code [ class "font-mono text-sm" ]
 | 
			
		||||
                [ text item.schedule
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2" ]
 | 
			
		||||
            [ text item.smtpConnection
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left hidden sm:table-cell mr-2" ]
 | 
			
		||||
            [ String.join ", " item.recipients |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center" ]
 | 
			
		||||
            [ String.fromInt item.remindDays
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,14 @@ module Comp.NotificationManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
 | 
			
		||||
import Api.Model.NotificationSettingsList exposing (NotificationSettingsList)
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.NotificationForm
 | 
			
		||||
import Comp.NotificationList
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
@@ -18,6 +20,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -253,3 +256,57 @@ viewForm settings model =
 | 
			
		||||
viewList : Model -> Html Msg
 | 
			
		||||
viewList model =
 | 
			
		||||
    Html.map ListMsg (Comp.NotificationList.view model.listModel model.items)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        ([ div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.errorMessage, Maybe.map .success model.result == Just False )
 | 
			
		||||
                , ( S.successMessage, Maybe.map .success model.result == Just True )
 | 
			
		||||
                , ( "hidden", model.result == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.result
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
         ]
 | 
			
		||||
            ++ (case model.detailModel of
 | 
			
		||||
                    Just msett ->
 | 
			
		||||
                        viewForm2 settings msett
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        viewList2 model
 | 
			
		||||
               )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Comp.NotificationForm.Model -> List (Html Msg)
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    [ Html.map DetailMsg
 | 
			
		||||
        (Comp.NotificationForm.view2 "flex flex-col" settings model)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewList2 : Model -> List (Html Msg)
 | 
			
		||||
viewList2 model =
 | 
			
		||||
    [ MB.view
 | 
			
		||||
        { start =
 | 
			
		||||
            [ MB.PrimaryButton
 | 
			
		||||
                { tagger = NewTask
 | 
			
		||||
                , label = "New Task"
 | 
			
		||||
                , icon = Just "fa fa-plus"
 | 
			
		||||
                , title = "Create a new notification task"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , end = []
 | 
			
		||||
        , rootClasses = "mb-4"
 | 
			
		||||
        }
 | 
			
		||||
    , Html.map ListMsg (Comp.NotificationList.view2 model.listModel model.items)
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,16 +7,19 @@ module Comp.OrgForm exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view1
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Organization exposing (Organization)
 | 
			
		||||
import Comp.AddressForm
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.ContactField
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -150,3 +153,62 @@ view1 settings compact model =
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Bool -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 mobile settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "orgname"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Name"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , name "orgname"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( S.inputErrorBorder, not (isValid model) )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class S.header3 ]
 | 
			
		||||
                [ text "Address"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map AddressMsg
 | 
			
		||||
                (Comp.AddressForm.view2 settings model.addressModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class S.header3 ]
 | 
			
		||||
                [ text "Contacts"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map ContactMsg
 | 
			
		||||
                (Comp.ContactField.view2 mobile settings model.contactModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class S.header3 ]
 | 
			
		||||
                [ text "Notes"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "" ]
 | 
			
		||||
                [ textarea
 | 
			
		||||
                    [ onInput SetNotes
 | 
			
		||||
                    , Maybe.withDefault "" model.notes |> value
 | 
			
		||||
                    , class S.textAreaInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,15 @@ module Comp.OrgManage exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.Organization
 | 
			
		||||
import Api.Model.OrganizationList exposing (OrganizationList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.OrgForm
 | 
			
		||||
import Comp.OrgTable
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
@@ -19,6 +22,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -301,3 +305,117 @@ viewForm settings model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
        viewTable2 model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewForm2 settings model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col relative" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewOrg
 | 
			
		||||
                    , title = "Create a new organization"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Organization"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.OrgTable.view2 model.tableModel)
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        newOrg =
 | 
			
		||||
            model.formModel.org.id == ""
 | 
			
		||||
 | 
			
		||||
        dimmerSettings2 =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this organization?"
 | 
			
		||||
    in
 | 
			
		||||
    Html.form
 | 
			
		||||
        [ class "md:relative flex flex-col"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings2
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , if newOrg then
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new organization"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text model.formModel.org.name
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text model.formModel.org.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newOrg then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this organization"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg (Comp.OrgForm.view2 False settings model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,16 @@ module Comp.OrgTable exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Organization exposing (Organization)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Address
 | 
			
		||||
import Util.Contact
 | 
			
		||||
 | 
			
		||||
@@ -88,3 +91,42 @@ renderOrgLine model org =
 | 
			
		||||
            [ Util.Contact.toString org.contacts |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left hidden md:table-cell" ] [ text "Address" ]
 | 
			
		||||
                , th [ class "text-left hidden sm:table-cell" ] [ text "Contact" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderOrgLine2 model) model.orgs)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderOrgLine2 : Model -> Organization -> Html Msg
 | 
			
		||||
renderOrgLine2 model org =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just org ) ]
 | 
			
		||||
        , class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (Select org)
 | 
			
		||||
        , td [ class "py-4 sm:py-2 pr-2 md:pr-4" ]
 | 
			
		||||
            [ text org.name
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "py-4 sm:py-2 pr-4 hidden md:table-cell" ]
 | 
			
		||||
            [ Util.Address.toString org.address |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "py-4 sm:py-2 sm:py-2 pr-2 md:pr-4 hidden sm:table-cell" ]
 | 
			
		||||
            [ Util.Contact.toString org.contacts |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,13 @@ module Comp.PasswordInput exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -72,3 +74,58 @@ view pw model =
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Maybe String -> Bool -> Model -> Html Msg
 | 
			
		||||
view2 pw isError model =
 | 
			
		||||
    div [ class "relative" ]
 | 
			
		||||
        [ div [ class S.inputIcon ]
 | 
			
		||||
            [ i
 | 
			
		||||
                [ class "fa"
 | 
			
		||||
                , if model.show then
 | 
			
		||||
                    class "fa-lock-open"
 | 
			
		||||
 | 
			
		||||
                  else
 | 
			
		||||
                    class "fa-lock"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , input
 | 
			
		||||
            [ type_ <|
 | 
			
		||||
                if model.show then
 | 
			
		||||
                    "text"
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    "password"
 | 
			
		||||
            , name "passw1"
 | 
			
		||||
            , autocomplete False
 | 
			
		||||
            , onInput SetPassword
 | 
			
		||||
            , Maybe.withDefault "" pw |> value
 | 
			
		||||
            , class ("pl-10 pr-10 py-2 rounded" ++ S.textInput)
 | 
			
		||||
            , class <|
 | 
			
		||||
                if isError then
 | 
			
		||||
                    S.inputErrorBorder
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    ""
 | 
			
		||||
            , placeholder "Password"
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , a
 | 
			
		||||
            [ class S.inputLeftIconLink
 | 
			
		||||
            , class <|
 | 
			
		||||
                if isError then
 | 
			
		||||
                    S.inputErrorBorder
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    ""
 | 
			
		||||
            , onClick (ToggleShow pw)
 | 
			
		||||
            , href "#"
 | 
			
		||||
            ]
 | 
			
		||||
            [ i [ class "fa fa-eye" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,18 +7,22 @@ module Comp.PersonForm exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view1
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.Person exposing (Person)
 | 
			
		||||
import Comp.AddressForm
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.ContactField
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -215,3 +219,94 @@ view1 settings compact model =
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Bool -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 mobile settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "mb-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                , for "personname"
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Name"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( S.inputErrorBorder, not (isValid model) )
 | 
			
		||||
                    ]
 | 
			
		||||
                , name "personname"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class "inline-flex items-center"
 | 
			
		||||
                , for "concerning"
 | 
			
		||||
                ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "checkbox"
 | 
			
		||||
                    , checked model.concerning
 | 
			
		||||
                    , onCheck SetConcerning
 | 
			
		||||
                    , class S.checkboxInput
 | 
			
		||||
                    , name "concerning"
 | 
			
		||||
                    , id "concerning"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ text "Use for concerning person suggestion only"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Organization"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map OrgDropdownMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.orgModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class "ui dividing header" ]
 | 
			
		||||
                [ text "Address"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map AddressMsg (Comp.AddressForm.view2 settings model.addressModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class S.header3 ]
 | 
			
		||||
                [ text "Contacts"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map ContactMsg
 | 
			
		||||
                (Comp.ContactField.view2 mobile settings model.contactModel)
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ h3 [ class S.header3 ]
 | 
			
		||||
                [ text "Notes"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "" ]
 | 
			
		||||
                [ textarea
 | 
			
		||||
                    [ onInput SetNotes
 | 
			
		||||
                    , Maybe.withDefault "" model.notes |> value
 | 
			
		||||
                    , class S.textAreaInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ module Comp.PersonManage exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -11,6 +12,8 @@ import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.Person
 | 
			
		||||
import Api.Model.PersonList exposing (PersonList)
 | 
			
		||||
import Api.Model.ReferenceList exposing (ReferenceList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.PersonForm
 | 
			
		||||
import Comp.PersonTable
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
@@ -20,6 +23,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -349,3 +353,117 @@ viewForm settings model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
        viewTable2 model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewForm2 settings model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewPerson
 | 
			
		||||
                    , title = "Create a new person"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Person"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.PersonTable.view2 model.tableModel)
 | 
			
		||||
        , B.loadingDimmer (isLoading model)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        newPerson =
 | 
			
		||||
            model.formModel.person.id == ""
 | 
			
		||||
 | 
			
		||||
        dimmerSettings2 =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this person?"
 | 
			
		||||
    in
 | 
			
		||||
    Html.form
 | 
			
		||||
        [ class "md:relative flex flex-col"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings2
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , if newPerson then
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new person"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text model.formModel.person.name
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text model.formModel.person.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newPerson then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this person"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg (Comp.PersonForm.view2 False settings model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer (isLoading model)
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,19 @@ module Comp.PersonTable exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Person exposing (Person)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Address
 | 
			
		||||
import Util.Contact
 | 
			
		||||
import Util.Html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -102,3 +106,50 @@ renderPersonLine model person =
 | 
			
		||||
            [ Util.Contact.toString person.contacts |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "w-px whitespace-nowrap" ] []
 | 
			
		||||
                , th [ class "w-px whitespace-nowrap text-center pr-1 md:px-2" ]
 | 
			
		||||
                    [ text "Concerning"
 | 
			
		||||
                    ]
 | 
			
		||||
                , th [ class "text-left" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left hidden sm:table-cell" ] [ text "Organization" ]
 | 
			
		||||
                , th [ class "text-left hidden md:table-cell" ] [ text "Contact" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderPersonLine2 model) model.equips)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderPersonLine2 : Model -> Person -> Html Msg
 | 
			
		||||
renderPersonLine2 model person =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just person ) ]
 | 
			
		||||
        , class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (Select person)
 | 
			
		||||
        , td [ class "w-px whitespace-nowrap text-center" ]
 | 
			
		||||
            [ Util.Html.checkbox2 person.concerning
 | 
			
		||||
            ]
 | 
			
		||||
        , td []
 | 
			
		||||
            [ text person.name
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden sm:table-cell" ]
 | 
			
		||||
            [ Maybe.map .name person.organization
 | 
			
		||||
                |> Maybe.withDefault "-"
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden md:table-cell" ]
 | 
			
		||||
            [ Util.Contact.toString person.contacts |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
module Comp.Progress exposing
 | 
			
		||||
    ( smallIndicating
 | 
			
		||||
    ( progress2
 | 
			
		||||
    , smallIndicating
 | 
			
		||||
    , topAttachedIndicating
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@@ -7,6 +8,21 @@ import Html exposing (Html, div, text)
 | 
			
		||||
import Html.Attributes exposing (attribute, class, style)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
progress2 : Int -> Html msg
 | 
			
		||||
progress2 percent =
 | 
			
		||||
    div [ class "shadow w-full h-full bg-gray-200 dark:bg-bluegray-600 rounded relative" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "transition-duration-300 h-full bg-blue-500 dark:bg-light-blue-500 block text-xs text-center"
 | 
			
		||||
            , style "width" (String.fromInt percent ++ "%")
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , div [ class "absolute left-1/2 -top-1 font-semibold" ]
 | 
			
		||||
            [ text (String.fromInt percent)
 | 
			
		||||
            , text "%"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
smallIndicating : Int -> Html msg
 | 
			
		||||
smallIndicating percent =
 | 
			
		||||
    progress "small indicating active" percent Nothing Nothing
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ module Comp.ScanMailboxForm exposing
 | 
			
		||||
    , initWith
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -18,14 +19,18 @@ import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
 | 
			
		||||
import Api.Model.StringList exposing (StringList)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Api.Model.TagList exposing (TagList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.CalEventInput
 | 
			
		||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.StringListInput
 | 
			
		||||
import Comp.Tabs
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.CalEvent exposing (CalEvent)
 | 
			
		||||
import Data.Direction exposing (Direction(..))
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Language exposing (Language)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -35,6 +40,8 @@ import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Markdown
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Folder exposing (mkFolderOption)
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.List
 | 
			
		||||
@@ -70,6 +77,7 @@ type alias Model =
 | 
			
		||||
    , language : Maybe Language
 | 
			
		||||
    , postHandleAll : Bool
 | 
			
		||||
    , menuTab : MenuTab
 | 
			
		||||
    , openTabs : Set String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -115,6 +123,7 @@ type Msg
 | 
			
		||||
    | RemoveLanguage
 | 
			
		||||
    | TogglePostHandleAll
 | 
			
		||||
    | SetMenuTab MenuTab
 | 
			
		||||
    | ToggleAkkordionTab String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
 | 
			
		||||
@@ -217,6 +226,7 @@ init flags =
 | 
			
		||||
      , language = Nothing
 | 
			
		||||
      , postHandleAll = False
 | 
			
		||||
      , menuTab = TabGeneral
 | 
			
		||||
      , openTabs = Set.insert (tabTitle TabGeneral) Set.empty
 | 
			
		||||
      }
 | 
			
		||||
    , Cmd.batch
 | 
			
		||||
        [ Api.getImapSettings flags "" ConnResp
 | 
			
		||||
@@ -676,6 +686,20 @@ update flags msg model =
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        ToggleAkkordionTab title ->
 | 
			
		||||
            let
 | 
			
		||||
                tabs =
 | 
			
		||||
                    if Set.member title model.openTabs then
 | 
			
		||||
                        Set.remove title model.openTabs
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Set.insert title model.openTabs
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | openTabs = tabs }
 | 
			
		||||
            , NoAction
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
@@ -694,6 +718,21 @@ isFormSuccess model =
 | 
			
		||||
        |> Maybe.withDefault False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isFolderMember : Model -> Bool
 | 
			
		||||
isFolderMember model =
 | 
			
		||||
    let
 | 
			
		||||
        selected =
 | 
			
		||||
            Comp.Dropdown.getSelected model.folderModel
 | 
			
		||||
                |> List.head
 | 
			
		||||
                |> Maybe.map .id
 | 
			
		||||
    in
 | 
			
		||||
    Util.Folder.isFolderMember model.allFolders selected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : String -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view extraClasses settings model =
 | 
			
		||||
    div
 | 
			
		||||
@@ -1106,12 +1145,455 @@ viewSchedule model =
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isFolderMember : Model -> Bool
 | 
			
		||||
isFolderMember model =
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 extraClasses settings model =
 | 
			
		||||
    let
 | 
			
		||||
        selected =
 | 
			
		||||
            Comp.Dropdown.getSelected model.folderModel
 | 
			
		||||
                |> List.head
 | 
			
		||||
                |> Maybe.map .id
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this scan mailbox task?"
 | 
			
		||||
 | 
			
		||||
        startOnceBtn =
 | 
			
		||||
            MB.SecondaryButton
 | 
			
		||||
                { tagger = StartOnce
 | 
			
		||||
                , label = "Start Once"
 | 
			
		||||
                , title = "Start this task now"
 | 
			
		||||
                , icon = Just "fa fa-play"
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        tabActive t =
 | 
			
		||||
            if Set.member t.title model.openTabs then
 | 
			
		||||
                ( Comp.Tabs.Open, ToggleAkkordionTab t.title )
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                ( Comp.Tabs.Closed, ToggleAkkordionTab t.title )
 | 
			
		||||
    in
 | 
			
		||||
    Util.Folder.isFolderMember model.allFolders selected
 | 
			
		||||
    div
 | 
			
		||||
        [ class extraClasses
 | 
			
		||||
        , class "md:relative"
 | 
			
		||||
        ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    , title = "Save"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = Cancel
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if model.settings.id /= "" then
 | 
			
		||||
                    [ startOnceBtn
 | 
			
		||||
                    , MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        , title = "Delete this task"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    [ startOnceBtn
 | 
			
		||||
                    ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.successMessage, isFormSuccess model )
 | 
			
		||||
                , ( S.errorMessage, isFormError model )
 | 
			
		||||
                , ( "hidden", model.formMsg == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.formMsg
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , Comp.Tabs.akkordion
 | 
			
		||||
            Comp.Tabs.defaultStyle
 | 
			
		||||
            tabActive
 | 
			
		||||
            (formTabs settings model)
 | 
			
		||||
        , Html.map YesNoDeleteMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.yesNoDelete
 | 
			
		||||
            )
 | 
			
		||||
        , B.loadingDimmer (model.loading > 0)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tabTitle : MenuTab -> String
 | 
			
		||||
tabTitle tab =
 | 
			
		||||
    case tab of
 | 
			
		||||
        TabGeneral ->
 | 
			
		||||
            "General"
 | 
			
		||||
 | 
			
		||||
        TabProcessing ->
 | 
			
		||||
            "Processing"
 | 
			
		||||
 | 
			
		||||
        TabAdditionalFilter ->
 | 
			
		||||
            "Additional Filter"
 | 
			
		||||
 | 
			
		||||
        TabPostProcessing ->
 | 
			
		||||
            "Post Processing"
 | 
			
		||||
 | 
			
		||||
        TabMetadata ->
 | 
			
		||||
            "Metadata"
 | 
			
		||||
 | 
			
		||||
        TabSchedule ->
 | 
			
		||||
            "Schedule"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
formTabs : UiSettings -> Model -> List (Comp.Tabs.Tab Msg)
 | 
			
		||||
formTabs settings model =
 | 
			
		||||
    [ { title = tabTitle TabGeneral
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body = viewGeneral2 settings model
 | 
			
		||||
      }
 | 
			
		||||
    , { title = tabTitle TabProcessing
 | 
			
		||||
      , info = Just "These settings define which mails are fetched from the mail server."
 | 
			
		||||
      , body = viewProcessing2 model
 | 
			
		||||
      }
 | 
			
		||||
    , { title = tabTitle TabAdditionalFilter
 | 
			
		||||
      , info = Just "These filters are applied to mails that have been fetched from the mailbox to select those that should be imported."
 | 
			
		||||
      , body = viewAdditionalFilter2 model
 | 
			
		||||
      }
 | 
			
		||||
    , { title = tabTitle TabPostProcessing
 | 
			
		||||
      , info = Just "This defines what happens to mails that have been downloaded."
 | 
			
		||||
      , body = viewPostProcessing2 model
 | 
			
		||||
      }
 | 
			
		||||
    , { title = tabTitle TabMetadata
 | 
			
		||||
      , info = Just "Define metadata that should be attached to all items created by this task."
 | 
			
		||||
      , body = viewMetadata2 settings model
 | 
			
		||||
      }
 | 
			
		||||
    , { title = tabTitle TabSchedule
 | 
			
		||||
      , info = Just "Define when mails should be imported."
 | 
			
		||||
      , body = viewSchedule2 model
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewGeneral2 : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewGeneral2 settings model =
 | 
			
		||||
    [ MB.viewItem <|
 | 
			
		||||
        MB.Checkbox
 | 
			
		||||
            { id = "scanmail-enabled"
 | 
			
		||||
            , value = model.enabled
 | 
			
		||||
            , tagger = \_ -> ToggleEnabled
 | 
			
		||||
            , label = "Enable or disable this task."
 | 
			
		||||
            }
 | 
			
		||||
    , div [ class "mb-4 mt-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Mailbox"
 | 
			
		||||
            , B.inputRequired
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map ConnMsg
 | 
			
		||||
            (Comp.Dropdown.view2
 | 
			
		||||
                DS.mainStyle
 | 
			
		||||
                settings
 | 
			
		||||
                model.connectionModel
 | 
			
		||||
            )
 | 
			
		||||
        , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "The IMAP connection to use when sending notification mails."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewProcessing2 : Model -> List (Html Msg)
 | 
			
		||||
viewProcessing2 model =
 | 
			
		||||
    [ div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Folders"
 | 
			
		||||
            , B.inputRequired
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map FoldersMsg
 | 
			
		||||
            (Comp.StringListInput.view2
 | 
			
		||||
                model.folders
 | 
			
		||||
                model.foldersModel
 | 
			
		||||
            )
 | 
			
		||||
        , span [ class "opacity-50 text-sm mt-1" ]
 | 
			
		||||
            [ text "The folders to look for mails."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , Html.map ReceivedHoursMsg
 | 
			
		||||
        (Comp.IntField.viewWithInfo2
 | 
			
		||||
            "Select mails newer than `now - receivedHours`"
 | 
			
		||||
            model.receivedHours
 | 
			
		||||
            "mb-4"
 | 
			
		||||
            model.receivedHoursModel
 | 
			
		||||
        )
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewAdditionalFilter2 : Model -> List (Html Msg)
 | 
			
		||||
viewAdditionalFilter2 model =
 | 
			
		||||
    [ div
 | 
			
		||||
        [ class "mb-4"
 | 
			
		||||
        ]
 | 
			
		||||
        [ label
 | 
			
		||||
            [ class S.inputLabel
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "File Filter" ]
 | 
			
		||||
        , input
 | 
			
		||||
            [ type_ "text"
 | 
			
		||||
            , onInput SetFileFilter
 | 
			
		||||
            , placeholder "File Filter"
 | 
			
		||||
            , model.fileFilter
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> value
 | 
			
		||||
            , class S.textInput
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Specify a file glob to filter attachments. For example, to only extract pdf files: "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "*.pdf"
 | 
			
		||||
                ]
 | 
			
		||||
            , text ". If you want to include the mail body, allow html files or "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "mail.html"
 | 
			
		||||
                ]
 | 
			
		||||
            , text ". Globs can be combined via OR, like this: "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "*.pdf|mail.html"
 | 
			
		||||
                ]
 | 
			
		||||
            , text ". No file filter defaults to "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "*"
 | 
			
		||||
                ]
 | 
			
		||||
            , text " that includes all"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div
 | 
			
		||||
        [ class "mb-4"
 | 
			
		||||
        ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Subject Filter" ]
 | 
			
		||||
        , input
 | 
			
		||||
            [ type_ "text"
 | 
			
		||||
            , onInput SetSubjectFilter
 | 
			
		||||
            , placeholder "Subject Filter"
 | 
			
		||||
            , model.subjectFilter
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> value
 | 
			
		||||
            , class S.textInput
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Specify a file glob to filter mails by subject. For example: "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "*Scanned Document*"
 | 
			
		||||
                ]
 | 
			
		||||
            , text ". No file filter defaults to "
 | 
			
		||||
            , code [ class "font-mono" ]
 | 
			
		||||
                [ text "*"
 | 
			
		||||
                ]
 | 
			
		||||
            , text " that includes all"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewPostProcessing2 : Model -> List (Html Msg)
 | 
			
		||||
viewPostProcessing2 model =
 | 
			
		||||
    [ div [ class "mb-4" ]
 | 
			
		||||
        [ MB.viewItem <|
 | 
			
		||||
            MB.Checkbox
 | 
			
		||||
                { id = "scanmail-posthandle-all"
 | 
			
		||||
                , value = model.postHandleAll
 | 
			
		||||
                , label = "Apply post-processing to all fetched mails."
 | 
			
		||||
                , tagger = \_ -> TogglePostHandleAll
 | 
			
		||||
                }
 | 
			
		||||
        , span [ class "opacity-50 text-sm mt-1" ]
 | 
			
		||||
            [ text "When mails are fetched but not imported due to the 'Additional Filters', this flag can "
 | 
			
		||||
            , text "control whether they should be moved to a target folder or deleted (whatever is "
 | 
			
		||||
            , text "defined here) nevertheless. If unchecked only imported mails "
 | 
			
		||||
            , text "are post-processed, others stay where they are."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Target folder"
 | 
			
		||||
            ]
 | 
			
		||||
        , input
 | 
			
		||||
            [ type_ "text"
 | 
			
		||||
            , onInput SetTargetFolder
 | 
			
		||||
            , Maybe.withDefault "" model.targetFolder |> value
 | 
			
		||||
            , class S.textInput
 | 
			
		||||
            ]
 | 
			
		||||
            []
 | 
			
		||||
        , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Move mails into this folder."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "mb-4" ]
 | 
			
		||||
        [ MB.viewItem <|
 | 
			
		||||
            MB.Checkbox
 | 
			
		||||
                { id = "scanmail-delete-all"
 | 
			
		||||
                , label = "Delete imported mails"
 | 
			
		||||
                , tagger = \_ -> ToggleDeleteMail
 | 
			
		||||
                , value = model.deleteMail
 | 
			
		||||
                }
 | 
			
		||||
        , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Whether to delete all mails fetched by docspell. This only applies if "
 | 
			
		||||
            , em [] [ text "target folder" ]
 | 
			
		||||
            , text " is not set."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewMetadata2 : UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewMetadata2 settings model =
 | 
			
		||||
    [ div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Item direction"
 | 
			
		||||
            , B.inputRequired
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-col " ]
 | 
			
		||||
            [ label [ class "inline-flex items-center" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "radio"
 | 
			
		||||
                    , checked (model.direction == Nothing)
 | 
			
		||||
                    , onCheck (\_ -> DirectionMsg Nothing)
 | 
			
		||||
                    , class S.radioInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "ml-2" ] [ text "Automatic" ]
 | 
			
		||||
                ]
 | 
			
		||||
            , label [ class "inline-flex items-center" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "radio"
 | 
			
		||||
                    , checked (model.direction == Just Incoming)
 | 
			
		||||
                    , class S.radioInput
 | 
			
		||||
                    , onCheck (\_ -> DirectionMsg (Just Incoming))
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "ml-2" ] [ text "Incoming" ]
 | 
			
		||||
                ]
 | 
			
		||||
            , label [ class "inline-flex items-center" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "radio"
 | 
			
		||||
                    , checked (model.direction == Just Outgoing)
 | 
			
		||||
                    , onCheck (\_ -> DirectionMsg (Just Outgoing))
 | 
			
		||||
                    , class S.radioInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "ml-2" ] [ text "Outgoing" ]
 | 
			
		||||
                ]
 | 
			
		||||
            , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Sets the direction for an item. If you know all mails are incoming or "
 | 
			
		||||
                , text "outgoing, you can set it here. Otherwise it will be guessed from looking "
 | 
			
		||||
                , text "at sender and receiver."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Item Folder"
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map FolderDropdownMsg
 | 
			
		||||
            (Comp.Dropdown.view2
 | 
			
		||||
                DS.mainStyle
 | 
			
		||||
                settings
 | 
			
		||||
                model.folderModel
 | 
			
		||||
            )
 | 
			
		||||
        , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Put all items from this mailbox into the selected folder"
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", isFolderMember model )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.message
 | 
			
		||||
            ]
 | 
			
		||||
            [ Markdown.toHtml [] """
 | 
			
		||||
You are **not a member** of this folder. Items created from mails in
 | 
			
		||||
this mailbox will be **hidden** from any search results. Use a folder
 | 
			
		||||
where you are a member of to make items visible. This message will
 | 
			
		||||
disappear then.
 | 
			
		||||
                      """
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Tags" ]
 | 
			
		||||
        , Html.map TagDropdownMsg
 | 
			
		||||
            (Comp.Dropdown.view2
 | 
			
		||||
                DS.mainStyle
 | 
			
		||||
                settings
 | 
			
		||||
                model.tagModel
 | 
			
		||||
            )
 | 
			
		||||
        , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Choose tags that should be applied to items."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Language"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row" ]
 | 
			
		||||
            [ Html.map LanguageMsg
 | 
			
		||||
                (Comp.FixedDropdown.viewStyled2
 | 
			
		||||
                    (DS.mainStyleWith "flex-grow mr-2")
 | 
			
		||||
                    False
 | 
			
		||||
                    (Maybe.map mkLanguageItem model.language)
 | 
			
		||||
                    model.languageModel
 | 
			
		||||
                )
 | 
			
		||||
            , a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , onClick RemoveLanguage
 | 
			
		||||
                , class S.secondaryBasicButton
 | 
			
		||||
                , class "flex-none"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-trash" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Used for text extraction and text analysis. The "
 | 
			
		||||
            , text "collective's default language is used, if not specified here."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewSchedule2 : Model -> List (Html Msg)
 | 
			
		||||
viewSchedule2 model =
 | 
			
		||||
    [ div [ class "mb-4" ]
 | 
			
		||||
        [ label [ class S.inputLabel ]
 | 
			
		||||
            [ text "Schedule"
 | 
			
		||||
            , B.inputRequired
 | 
			
		||||
            , a
 | 
			
		||||
                [ class "float-right"
 | 
			
		||||
                , class S.link
 | 
			
		||||
                , href "https://github.com/eikek/calev#what-are-calendar-events"
 | 
			
		||||
                , target "_blank"
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-question" ] []
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ text "Click here for help"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map CalEventMsg
 | 
			
		||||
            (Comp.CalEventInput.view2 ""
 | 
			
		||||
                (Data.Validated.value model.schedule)
 | 
			
		||||
                model.scheduleModel
 | 
			
		||||
            )
 | 
			
		||||
        , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
            [ text "Specify how often and when this task should run. "
 | 
			
		||||
            , text "Use English 3-letter weekdays. Either a single value, "
 | 
			
		||||
            , text "a list (ex. 1,2,3), a range (ex. 1..3) or a '*' (meaning all) "
 | 
			
		||||
            , text "is allowed for each part."
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,15 @@ module Comp.ScanMailboxList exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +42,10 @@ update msg model =
 | 
			
		||||
            ( model, EditAction settings )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List ScanMailboxSettings -> Html Msg
 | 
			
		||||
view _ items =
 | 
			
		||||
    div []
 | 
			
		||||
@@ -104,3 +111,65 @@ viewItem item =
 | 
			
		||||
            [ Util.Html.checkbox item.deleteMail
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> List ScanMailboxSettings -> Html Msg
 | 
			
		||||
view2 _ items =
 | 
			
		||||
    div []
 | 
			
		||||
        [ table [ class S.tableMain ]
 | 
			
		||||
            [ thead []
 | 
			
		||||
                [ tr []
 | 
			
		||||
                    [ th [ class "" ] []
 | 
			
		||||
                    , th [ class "" ]
 | 
			
		||||
                        [ i [ class "fa fa-check" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    , th [ class "text-left mr-2 hidden md:table-cell" ] [ text "Schedule" ]
 | 
			
		||||
                    , th [ class "text-left mr-2" ] [ text "Connection" ]
 | 
			
		||||
                    , th [ class "text-left mr-2" ] [ text "Folders" ]
 | 
			
		||||
                    , th [ class "text-left mr-2 hidden md:table-cell" ] [ text "Received Since" ]
 | 
			
		||||
                    , th [ class "text-left mr-2 hidden md:table-cell" ] [ text "Target" ]
 | 
			
		||||
                    , th [ class "hidden md:table-cell" ] [ text "Delete" ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , tbody []
 | 
			
		||||
                (List.map viewItem2 items)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem2 : ScanMailboxSettings -> Html Msg
 | 
			
		||||
viewItem2 item =
 | 
			
		||||
    tr [ class S.tableRow ]
 | 
			
		||||
        [ B.editLinkTableCell (EditSettings item)
 | 
			
		||||
        , td [ class "w-px px-2" ]
 | 
			
		||||
            [ Util.Html.checkbox2 item.enabled
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "mr-2 hidden md:table-cell" ]
 | 
			
		||||
            [ code [ class "font-mono text-sm" ]
 | 
			
		||||
                [ text item.schedule
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2" ]
 | 
			
		||||
            [ text item.imapConnection
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2" ]
 | 
			
		||||
            [ String.join ", " item.folders |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2 hidden md:table-cell" ]
 | 
			
		||||
            [ Maybe.map String.fromInt item.receivedSinceHours
 | 
			
		||||
                |> Maybe.withDefault "-"
 | 
			
		||||
                |> text
 | 
			
		||||
            , text " h"
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left mr-2 hidden md:table-cell" ]
 | 
			
		||||
            [ Maybe.withDefault "-" item.targetFolder
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "w-px px-2 hidden md:table-cell" ]
 | 
			
		||||
            [ Util.Html.checkbox2 item.deleteMail
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,14 @@ module Comp.ScanMailboxManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
 | 
			
		||||
import Api.Model.ScanMailboxSettingsList exposing (ScanMailboxSettingsList)
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.ScanMailboxForm
 | 
			
		||||
import Comp.ScanMailboxList
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
@@ -18,6 +20,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -253,3 +256,57 @@ viewForm settings model =
 | 
			
		||||
viewList : Model -> Html Msg
 | 
			
		||||
viewList model =
 | 
			
		||||
    Html.map ListMsg (Comp.ScanMailboxList.view model.listModel model.items)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        ([ div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.errorMessage, Maybe.map .success model.result == Just False )
 | 
			
		||||
                , ( S.successMessage, Maybe.map .success model.result == Just True )
 | 
			
		||||
                , ( "hidden", model.result == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.result
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
         ]
 | 
			
		||||
            ++ (case model.detailModel of
 | 
			
		||||
                    Just msett ->
 | 
			
		||||
                        viewForm2 settings msett
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        viewList2 model
 | 
			
		||||
               )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Comp.ScanMailboxForm.Model -> List (Html Msg)
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    [ Html.map DetailMsg
 | 
			
		||||
        (Comp.ScanMailboxForm.view2 "" settings model)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewList2 : Model -> List (Html Msg)
 | 
			
		||||
viewList2 model =
 | 
			
		||||
    [ MB.view
 | 
			
		||||
        { start =
 | 
			
		||||
            [ MB.PrimaryButton
 | 
			
		||||
                { tagger = NewTask
 | 
			
		||||
                , label = "New Task"
 | 
			
		||||
                , icon = Just "fa fa-plus"
 | 
			
		||||
                , title = "Create a new scan mailbox task"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , end = []
 | 
			
		||||
        , rootClasses = "mb-4"
 | 
			
		||||
        }
 | 
			
		||||
    , Html.map ListMsg (Comp.ScanMailboxList.view2 model.listModel model.items)
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ module Comp.SearchMenu exposing
 | 
			
		||||
    , updateDrop
 | 
			
		||||
    , view
 | 
			
		||||
    , viewDrop
 | 
			
		||||
    , viewDrop2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -24,14 +25,16 @@ import Api.Model.ItemSearch exposing (ItemSearch)
 | 
			
		||||
import Api.Model.PersonList exposing (PersonList)
 | 
			
		||||
import Api.Model.ReferenceList exposing (ReferenceList)
 | 
			
		||||
import Api.Model.SearchStats exposing (SearchStats)
 | 
			
		||||
import Api.Model.TagList exposing (TagList)
 | 
			
		||||
import Comp.CustomFieldMultiInput
 | 
			
		||||
import Comp.DatePicker
 | 
			
		||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
 | 
			
		||||
import Comp.FolderSelect
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.Tabs
 | 
			
		||||
import Comp.TagSelect
 | 
			
		||||
import Data.CustomFieldChange exposing (CustomFieldValueCollect)
 | 
			
		||||
import Data.Direction exposing (Direction)
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Fields
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
@@ -41,6 +44,8 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html exposing (KeyCode(..))
 | 
			
		||||
import Util.ItemDragDrop as DD
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
@@ -75,6 +80,7 @@ type alias Model =
 | 
			
		||||
    , customFieldModel : Comp.CustomFieldMultiInput.Model
 | 
			
		||||
    , customValues : CustomFieldValueCollect
 | 
			
		||||
    , sourceModel : Maybe String
 | 
			
		||||
    , openTabs : Set String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -141,6 +147,7 @@ init flags =
 | 
			
		||||
    , customFieldModel = Comp.CustomFieldMultiInput.initWith []
 | 
			
		||||
    , customValues = Data.CustomFieldChange.emptyCollect
 | 
			
		||||
    , sourceModel = Nothing
 | 
			
		||||
    , openTabs = Set.fromList [ "Tags", "Inbox" ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -361,6 +368,8 @@ type Msg
 | 
			
		||||
    | ResetToSource String
 | 
			
		||||
    | GetStatsResp (Result Http.Error SearchStats)
 | 
			
		||||
    | GetAllTagsResp (Result Http.Error SearchStats)
 | 
			
		||||
    | ToggleAkkordionTab String
 | 
			
		||||
    | ToggleOpenAllAkkordionTabs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias NextState =
 | 
			
		||||
@@ -838,7 +847,7 @@ updateDrop ddm flags settings msg model =
 | 
			
		||||
        CustomFieldMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                res =
 | 
			
		||||
                    Comp.CustomFieldMultiInput.updateSearch lm model.customFieldModel
 | 
			
		||||
                    Comp.CustomFieldMultiInput.updateSearch flags lm model.customFieldModel
 | 
			
		||||
            in
 | 
			
		||||
            { model =
 | 
			
		||||
                { model
 | 
			
		||||
@@ -881,6 +890,41 @@ updateDrop ddm flags settings msg model =
 | 
			
		||||
        ResetToSource str ->
 | 
			
		||||
            resetAndSet (SetSource str)
 | 
			
		||||
 | 
			
		||||
        ToggleAkkordionTab title ->
 | 
			
		||||
            let
 | 
			
		||||
                tabs =
 | 
			
		||||
                    if Set.member title model.openTabs then
 | 
			
		||||
                        Set.remove title model.openTabs
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Set.insert title model.openTabs
 | 
			
		||||
            in
 | 
			
		||||
            { model = { model | openTabs = tabs }
 | 
			
		||||
            , cmd = Cmd.none
 | 
			
		||||
            , stateChange = False
 | 
			
		||||
            , dragDrop = DD.DragDropData ddm Nothing
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        ToggleOpenAllAkkordionTabs ->
 | 
			
		||||
            let
 | 
			
		||||
                allNames =
 | 
			
		||||
                    searchTabs (DD.DragDropData ddm Nothing) flags settings model
 | 
			
		||||
                        |> List.map .title
 | 
			
		||||
                        |> Set.fromList
 | 
			
		||||
 | 
			
		||||
                next =
 | 
			
		||||
                    if model.openTabs == allNames then
 | 
			
		||||
                        Set.empty
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        allNames
 | 
			
		||||
            in
 | 
			
		||||
            { model = { model | openTabs = next }
 | 
			
		||||
            , cmd = Cmd.none
 | 
			
		||||
            , stateChange = False
 | 
			
		||||
            , dragDrop = DD.DragDropData ddm Nothing
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- View
 | 
			
		||||
@@ -1163,3 +1207,353 @@ viewDrop ddd flags settings model =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewDrop2 : DD.DragDropData -> Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
viewDrop2 ddd flags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        akkordionStyle =
 | 
			
		||||
            Comp.Tabs.searchMenuStyle
 | 
			
		||||
    in
 | 
			
		||||
    Comp.Tabs.akkordion
 | 
			
		||||
        akkordionStyle
 | 
			
		||||
        (searchTabState settings model)
 | 
			
		||||
        (searchTabs ddd flags settings model)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
searchTabState : UiSettings -> Model -> Comp.Tabs.Tab Msg -> ( Comp.Tabs.State, Msg )
 | 
			
		||||
searchTabState settings model tab =
 | 
			
		||||
    let
 | 
			
		||||
        isHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        hidden =
 | 
			
		||||
            case tab.title of
 | 
			
		||||
                "Tags" ->
 | 
			
		||||
                    isHidden Data.Fields.Tag
 | 
			
		||||
 | 
			
		||||
                "Tag Categories" ->
 | 
			
		||||
                    isHidden Data.Fields.Tag
 | 
			
		||||
 | 
			
		||||
                "Folder" ->
 | 
			
		||||
                    isHidden Data.Fields.Folder
 | 
			
		||||
 | 
			
		||||
                "Correspondent" ->
 | 
			
		||||
                    isHidden Data.Fields.CorrOrg && isHidden Data.Fields.CorrPerson
 | 
			
		||||
 | 
			
		||||
                "Concerning" ->
 | 
			
		||||
                    isHidden Data.Fields.ConcEquip && isHidden Data.Fields.ConcPerson
 | 
			
		||||
 | 
			
		||||
                "Custom Fields" ->
 | 
			
		||||
                    isHidden Data.Fields.CustomFields
 | 
			
		||||
                        || Comp.CustomFieldMultiInput.isEmpty model.customFieldModel
 | 
			
		||||
 | 
			
		||||
                "Date" ->
 | 
			
		||||
                    isHidden Data.Fields.Date
 | 
			
		||||
 | 
			
		||||
                "Due Date" ->
 | 
			
		||||
                    isHidden Data.Fields.DueDate
 | 
			
		||||
 | 
			
		||||
                "Source" ->
 | 
			
		||||
                    isHidden Data.Fields.SourceName
 | 
			
		||||
 | 
			
		||||
                "Direction" ->
 | 
			
		||||
                    isHidden Data.Fields.Direction
 | 
			
		||||
 | 
			
		||||
                _ ->
 | 
			
		||||
                    False
 | 
			
		||||
 | 
			
		||||
        state =
 | 
			
		||||
            if hidden then
 | 
			
		||||
                Comp.Tabs.Hidden
 | 
			
		||||
 | 
			
		||||
            else if Set.member tab.title model.openTabs then
 | 
			
		||||
                Comp.Tabs.Open
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                Comp.Tabs.Closed
 | 
			
		||||
    in
 | 
			
		||||
    ( state, ToggleAkkordionTab tab.title )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
searchTabs : DD.DragDropData -> Flags -> UiSettings -> Model -> List (Comp.Tabs.Tab Msg)
 | 
			
		||||
searchTabs ddd flags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        isHidden f =
 | 
			
		||||
            Data.UiSettings.fieldHidden settings f
 | 
			
		||||
 | 
			
		||||
        tagSelectWM =
 | 
			
		||||
            Comp.TagSelect.makeWorkModel model.tagSelection model.tagSelectModel
 | 
			
		||||
    in
 | 
			
		||||
    [ { title = "Inbox"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ MB.viewItem <|
 | 
			
		||||
                MB.Checkbox
 | 
			
		||||
                    { id = "search-inbox"
 | 
			
		||||
                    , value = model.inboxCheckbox
 | 
			
		||||
                    , label = "Inbox"
 | 
			
		||||
                    , tagger = \_ -> ToggleInbox
 | 
			
		||||
                    }
 | 
			
		||||
            , div [ class "mt-2 hidden" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text
 | 
			
		||||
                        (case model.textSearchModel of
 | 
			
		||||
                            Fulltext _ ->
 | 
			
		||||
                                "Fulltext Search"
 | 
			
		||||
 | 
			
		||||
                            Names _ ->
 | 
			
		||||
                                "Search in names"
 | 
			
		||||
                        )
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ classList
 | 
			
		||||
                            [ ( "hidden", not flags.config.fullTextSearchEnabled )
 | 
			
		||||
                            ]
 | 
			
		||||
                        , class "float-right"
 | 
			
		||||
                        , class S.link
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , onClick SwapTextSearch
 | 
			
		||||
                        , title "Switch between text search modes"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-exchange-alt" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , onInput SetTextSearch
 | 
			
		||||
                    , Util.Html.onKeyUpCode KeyUpMsg
 | 
			
		||||
                    , textSearchString model.textSearchModel |> Maybe.withDefault "" |> value
 | 
			
		||||
                    , case model.textSearchModel of
 | 
			
		||||
                        Fulltext _ ->
 | 
			
		||||
                            placeholder "Content search…"
 | 
			
		||||
 | 
			
		||||
                        Names _ ->
 | 
			
		||||
                            placeholder "Search in various names…"
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ case model.textSearchModel of
 | 
			
		||||
                        Fulltext _ ->
 | 
			
		||||
                            text "Fulltext search in document contents and notes."
 | 
			
		||||
 | 
			
		||||
                        Names _ ->
 | 
			
		||||
                            text "Looks in correspondents, concerned entities, item name and notes."
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Tags"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            List.map (Html.map TagSelectMsg)
 | 
			
		||||
                (Comp.TagSelect.viewTagsDrop2
 | 
			
		||||
                    ddd.model
 | 
			
		||||
                    tagSelectWM
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.tagSelectModel
 | 
			
		||||
                )
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Tag Categories"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map TagSelectMsg
 | 
			
		||||
                (Comp.TagSelect.viewCats2
 | 
			
		||||
                    settings
 | 
			
		||||
                    tagSelectWM
 | 
			
		||||
                    model.tagSelectModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Folder"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map FolderSelectMsg
 | 
			
		||||
                (Comp.FolderSelect.viewDrop2 ddd.model
 | 
			
		||||
                    settings.searchMenuFolderCount
 | 
			
		||||
                    model.folderList
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Correspondent"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "mb-4"
 | 
			
		||||
                , classList [ ( "hidden", isHidden Data.Fields.CorrOrg ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Organization" ]
 | 
			
		||||
                , Html.map OrgMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        DS.sidebarStyle
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.orgModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ class "mb-4"
 | 
			
		||||
                , classList [ ( "hidden", isHidden Data.Fields.CorrPerson ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ label [ class S.inputLabel ] [ text "Person" ]
 | 
			
		||||
                , Html.map CorrPersonMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        DS.sidebarStyle
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.corrPersonModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Concerning"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "mb-4"
 | 
			
		||||
                , classList [ ( "hidden", isHidden Data.Fields.ConcPerson ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ label [ class S.inputLabel ] [ text "Person" ]
 | 
			
		||||
                , Html.map ConcPersonMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        DS.sidebarStyle
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.concPersonModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ class "mb-4"
 | 
			
		||||
                , classList [ ( "hidden", isHidden Data.Fields.ConcEquip ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                [ label [ class S.inputLabel ] [ text "Equipment" ]
 | 
			
		||||
                , Html.map ConcEquipmentMsg
 | 
			
		||||
                    (Comp.Dropdown.view2
 | 
			
		||||
                        DS.sidebarStyle
 | 
			
		||||
                        settings
 | 
			
		||||
                        model.concEquipmentModel
 | 
			
		||||
                    )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Custom Fields"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map CustomFieldMsg
 | 
			
		||||
                (Comp.CustomFieldMultiInput.view2
 | 
			
		||||
                    DS.sidebarStyle
 | 
			
		||||
                    (Comp.CustomFieldMultiInput.ViewSettings False "field" (\_ -> Nothing))
 | 
			
		||||
                    model.customFieldModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Date"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "flex flex-col" ]
 | 
			
		||||
                [ div [ class "mb-2" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ text "From"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "relative" ]
 | 
			
		||||
                        [ Html.map FromDateMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                                model.fromDate
 | 
			
		||||
                                model.fromDateModel
 | 
			
		||||
                            )
 | 
			
		||||
                        , i
 | 
			
		||||
                            [ class S.dateInputIcon
 | 
			
		||||
                            , class "fa fa-calendar"
 | 
			
		||||
                            ]
 | 
			
		||||
                            []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "mb-2" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ text "To"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "relative" ]
 | 
			
		||||
                        [ Html.map UntilDateMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                                model.untilDate
 | 
			
		||||
                                model.untilDateModel
 | 
			
		||||
                            )
 | 
			
		||||
                        , i [ class S.dateInputIcon, class "fa fa-calendar" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Due Date"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "flex flex-col" ]
 | 
			
		||||
                [ div [ class "mb-2" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ text "Due From"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "relative" ]
 | 
			
		||||
                        [ Html.map FromDueDateMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                                model.fromDueDate
 | 
			
		||||
                                model.fromDueDateModel
 | 
			
		||||
                            )
 | 
			
		||||
                        , i
 | 
			
		||||
                            [ class "fa fa-calendar"
 | 
			
		||||
                            , class S.dateInputIcon
 | 
			
		||||
                            ]
 | 
			
		||||
                            []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "mb-2" ]
 | 
			
		||||
                    [ label [ class S.inputLabel ]
 | 
			
		||||
                        [ text "Due To"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "relative" ]
 | 
			
		||||
                        [ Html.map UntilDueDateMsg
 | 
			
		||||
                            (Comp.DatePicker.viewTimeDefault
 | 
			
		||||
                                model.untilDueDate
 | 
			
		||||
                                model.untilDueDateModel
 | 
			
		||||
                            )
 | 
			
		||||
                        , i
 | 
			
		||||
                            [ class "fa fa-calendar"
 | 
			
		||||
                            , class S.dateInputIcon
 | 
			
		||||
                            ]
 | 
			
		||||
                            []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Source"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , onInput SetSource
 | 
			
		||||
                    , Util.Html.onKeyUpCode KeyUpMsg
 | 
			
		||||
                    , model.sourceModel |> Maybe.withDefault "" |> value
 | 
			
		||||
                    , placeholder "Search in item source…"
 | 
			
		||||
                    , class S.textInputSidebar
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Direction"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map DirectionMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.sidebarStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.directionModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,17 @@ module Comp.SearchStatsView exposing
 | 
			
		||||
    ( nameOrLabel
 | 
			
		||||
    , sortFields
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.FieldStats exposing (FieldStats)
 | 
			
		||||
import Api.Model.SearchStats exposing (SearchStats)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Icons as Icons
 | 
			
		||||
import Data.Money
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
nameOrLabel : FieldStats -> String
 | 
			
		||||
@@ -17,6 +20,15 @@ nameOrLabel f =
 | 
			
		||||
    Maybe.withDefault f.name f.label
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sortFields : List FieldStats -> List FieldStats
 | 
			
		||||
sortFields fields =
 | 
			
		||||
    List.sortBy nameOrLabel fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : SearchStats -> List (Html msg)
 | 
			
		||||
view stats =
 | 
			
		||||
    let
 | 
			
		||||
@@ -85,6 +97,80 @@ view stats =
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sortFields : List FieldStats -> List FieldStats
 | 
			
		||||
sortFields fields =
 | 
			
		||||
    List.sortBy nameOrLabel fields
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : String -> SearchStats -> Html msg
 | 
			
		||||
view2 classes stats =
 | 
			
		||||
    let
 | 
			
		||||
        isNumField f =
 | 
			
		||||
            f.sum > 0
 | 
			
		||||
 | 
			
		||||
        statValues f =
 | 
			
		||||
            tr [ class "border-0 border-t  dark:border-bluegray-600" ]
 | 
			
		||||
                [ td [ class "text-left text-sm" ]
 | 
			
		||||
                    [ div
 | 
			
		||||
                        [ class S.basicLabel
 | 
			
		||||
                        , class "max-w-min"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ Icons.customFieldTypeIconString2 "" f.ftype
 | 
			
		||||
                        , span [ class "pl-2" ]
 | 
			
		||||
                            [ text (nameOrLabel f)
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , td [ class "text-center py-2" ]
 | 
			
		||||
                    [ f.count |> String.fromInt |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                , td [ class "text-center py-2" ]
 | 
			
		||||
                    [ f.sum |> Data.Money.format |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                , td [ class "text-center py-2 hidden md:table-cell" ]
 | 
			
		||||
                    [ f.avg |> Data.Money.format |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                , td [ class "text-center py-2 hidden md:table-cell" ]
 | 
			
		||||
                    [ f.min |> Data.Money.format |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                , td [ class "text-center py-2 hidden md:table-cell" ]
 | 
			
		||||
                    [ f.max |> Data.Money.format |> text
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        fields =
 | 
			
		||||
            List.filter isNumField stats.fieldStats
 | 
			
		||||
                |> sortFields
 | 
			
		||||
    in
 | 
			
		||||
    div [ class classes ]
 | 
			
		||||
        [ div [ class "flex flex-col md:flex-row" ]
 | 
			
		||||
            [ div [ class "px-8 py-4" ]
 | 
			
		||||
                [ B.stats
 | 
			
		||||
                    { rootClass = ""
 | 
			
		||||
                    , valueClass = "text-4xl"
 | 
			
		||||
                    , value = String.fromInt stats.count
 | 
			
		||||
                    , label = "Items"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex-grow" ]
 | 
			
		||||
                [ table [ class "w-full text-sm" ]
 | 
			
		||||
                    [ thead []
 | 
			
		||||
                        [ tr [ class "" ]
 | 
			
		||||
                            [ th [ class "py-2 text-left" ] []
 | 
			
		||||
                            , th [ class "py-2 text-center" ]
 | 
			
		||||
                                [ text "Count" ]
 | 
			
		||||
                            , th [ class "py-2 text-center" ]
 | 
			
		||||
                                [ text "Sum" ]
 | 
			
		||||
                            , th [ class "py-2 text-center hidden md:table-cell" ]
 | 
			
		||||
                                [ text "Avg" ]
 | 
			
		||||
                            , th [ class "py-2 text-center hidden md:table-cell" ]
 | 
			
		||||
                                [ text "Min" ]
 | 
			
		||||
                            , th [ class "py-2 text-center hidden md:table-cell" ]
 | 
			
		||||
                                [ text "Max" ]
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    , tbody []
 | 
			
		||||
                        (List.map statValues fields)
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,15 @@ module Comp.SentMails exposing
 | 
			
		||||
    , isEmpty
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.SentMail exposing (SentMail)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +56,10 @@ update msg model =
 | 
			
		||||
            { model | selected = Just m }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    case model.selected of
 | 
			
		||||
@@ -129,3 +136,105 @@ renderLine mail =
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "collapsing" ] [ text mail.sender ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    case model.selected of
 | 
			
		||||
        Just mail ->
 | 
			
		||||
            div [ class "flex flex-col" ]
 | 
			
		||||
                [ div [ class "text-sm flex-flex-col" ]
 | 
			
		||||
                    [ div [ class "flex flex-row" ]
 | 
			
		||||
                        [ span [ class "font-bold" ]
 | 
			
		||||
                            [ text "From:"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , div [ class "ml-2" ]
 | 
			
		||||
                            [ text mail.sender
 | 
			
		||||
                            , text " ("
 | 
			
		||||
                            , text mail.connection
 | 
			
		||||
                            , text ")"
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "flex flex-row" ]
 | 
			
		||||
                        [ span [ class "font-bold" ]
 | 
			
		||||
                            [ text "Date:"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , div [ class "ml-2" ]
 | 
			
		||||
                            [ Util.Time.formatDateTime mail.created |> text
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "flex flex-row" ]
 | 
			
		||||
                        [ span [ class "font-bold" ]
 | 
			
		||||
                            [ text "Recipients:"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , div [ class "ml-2" ]
 | 
			
		||||
                            [ String.join ", " mail.recipients |> text
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "flex flex-row" ]
 | 
			
		||||
                        [ span [ class "font-bold" ]
 | 
			
		||||
                            [ text "Subject:"
 | 
			
		||||
                            ]
 | 
			
		||||
                        , div [ class "ml-2" ]
 | 
			
		||||
                            [ text mail.subject
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , hr [ class S.border ] []
 | 
			
		||||
                , div [ class "py-1 whitespace-pre-wrap" ]
 | 
			
		||||
                    [ text mail.body
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "flex flex-row items-center border-t dark:border-bluegray-600 justify-end text-sm " ]
 | 
			
		||||
                    [ a
 | 
			
		||||
                        [ class S.secondaryBasicButton
 | 
			
		||||
                        , onClick Hide
 | 
			
		||||
                        , class "mt-1"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-times" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            table [ class "border-collapse w-full" ]
 | 
			
		||||
                [ thead []
 | 
			
		||||
                    [ tr []
 | 
			
		||||
                        [ th [] []
 | 
			
		||||
                        , th [ class "text-left" ] [ text "Recipients" ]
 | 
			
		||||
                        , th [ class "hidden" ] [ text "Subject" ]
 | 
			
		||||
                        , th [ class "hidden text-center xl:table-cell" ] [ text "Sent" ]
 | 
			
		||||
                        , th [ class "hidden" ] [ text "Sender" ]
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , tbody [] <|
 | 
			
		||||
                    List.map
 | 
			
		||||
                        renderLine2
 | 
			
		||||
                        model.mails
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderLine2 : SentMail -> Html Msg
 | 
			
		||||
renderLine2 mail =
 | 
			
		||||
    tr [ class S.tableRow ]
 | 
			
		||||
        [ td []
 | 
			
		||||
            [ B.linkLabel
 | 
			
		||||
                { label = ""
 | 
			
		||||
                , icon = "fa fa-eye"
 | 
			
		||||
                , handler = Show mail
 | 
			
		||||
                , disabled = False
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left py-4 md:py-2" ]
 | 
			
		||||
            [ String.join ", " mail.recipients |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden" ] [ text mail.subject ]
 | 
			
		||||
        , td [ class "hidden text-center xl:table-cell" ]
 | 
			
		||||
            [ Util.Time.formatDateTime mail.created |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden" ] [ text mail.sender ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ module Comp.SourceForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -15,8 +16,10 @@ import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Api.Model.TagList exposing (TagList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Priority exposing (Priority)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -25,6 +28,7 @@ import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Markdown
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Folder exposing (mkFolderOption)
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Tag
 | 
			
		||||
@@ -64,7 +68,7 @@ emptyModel =
 | 
			
		||||
            }
 | 
			
		||||
    , allFolders = []
 | 
			
		||||
    , folderId = Nothing
 | 
			
		||||
    , tagModel = Util.Tag.makeDropdownModel
 | 
			
		||||
    , tagModel = Util.Tag.makeDropdownModel2
 | 
			
		||||
    , fileFilter = Nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -415,3 +419,177 @@ isFolderMember model =
 | 
			
		||||
                |> Maybe.map .id
 | 
			
		||||
    in
 | 
			
		||||
    Util.Folder.isFolderMember model.allFolders selected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 flags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        priorityItem =
 | 
			
		||||
            Comp.FixedDropdown.Item
 | 
			
		||||
                model.priority
 | 
			
		||||
                (Data.Priority.toName model.priority)
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "source-abbrev"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , id "source-abbrev"
 | 
			
		||||
                , onInput SetAbbrev
 | 
			
		||||
                , placeholder "Name"
 | 
			
		||||
                , value model.abbrev
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, not (isValid model) ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "source-descr"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Description"
 | 
			
		||||
                ]
 | 
			
		||||
            , textarea
 | 
			
		||||
                [ onInput SetDescr
 | 
			
		||||
                , model.description |> Maybe.withDefault "" |> value
 | 
			
		||||
                , rows 3
 | 
			
		||||
                , class S.textAreaInput
 | 
			
		||||
                , id "source-descr"
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class "inline-flex items-center"
 | 
			
		||||
                , for "source-enabled"
 | 
			
		||||
                ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "checkbox"
 | 
			
		||||
                    , onCheck (\_ -> ToggleEnabled)
 | 
			
		||||
                    , checked model.enabled
 | 
			
		||||
                    , class S.checkboxInput
 | 
			
		||||
                    , id "source-enabled"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ text "Enabled"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Priority"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map PrioDropdownMsg
 | 
			
		||||
                (Comp.FixedDropdown.view2
 | 
			
		||||
                    (Just priorityItem)
 | 
			
		||||
                    model.priorityModel
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "The priority used by the scheduler when processing uploaded files."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.header2
 | 
			
		||||
            , class "mt-6"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Metadata"
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class S.message
 | 
			
		||||
            , class "mb-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Metadata specified here is automatically attached to each item uploaded "
 | 
			
		||||
            , text "through this source, unless it is overriden in the upload request meta data. "
 | 
			
		||||
            , text "Tags from the request are added to those defined here."
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Folder"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map FolderDropdownMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.folderModel
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Choose a folder to automatically put items into."
 | 
			
		||||
                ]
 | 
			
		||||
            , div
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( "hidden", isFolderMember2 model )
 | 
			
		||||
                    ]
 | 
			
		||||
                , class S.message
 | 
			
		||||
                ]
 | 
			
		||||
                [ Markdown.toHtml [] """
 | 
			
		||||
You are **not a member** of this folder. Items created through this
 | 
			
		||||
link will be **hidden** from any search results. Use a folder where
 | 
			
		||||
you are a member of to make items visible. This message will
 | 
			
		||||
disappear then.
 | 
			
		||||
                      """
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Tags" ]
 | 
			
		||||
            , Html.map TagDropdownMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.tagModel
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Choose tags that should be applied to items."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "mb-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "File Filter" ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetFileFilter
 | 
			
		||||
                , placeholder "File Filter"
 | 
			
		||||
                , model.fileFilter
 | 
			
		||||
                    |> Maybe.withDefault ""
 | 
			
		||||
                    |> value
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Specify a file glob to filter files when uploading archives "
 | 
			
		||||
                , text "(e.g. for email and zip). For example, to only extract pdf files: "
 | 
			
		||||
                , code [ class "font-mono" ]
 | 
			
		||||
                    [ text "*.pdf"
 | 
			
		||||
                    ]
 | 
			
		||||
                , text ". Globs can be combined via OR, like this: "
 | 
			
		||||
                , code [ class "font-mono" ]
 | 
			
		||||
                    [ text "*.pdf|mail.html"
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isFolderMember2 : Model -> Bool
 | 
			
		||||
isFolderMember2 model =
 | 
			
		||||
    let
 | 
			
		||||
        selected =
 | 
			
		||||
            Comp.Dropdown.getSelected model.folderModel
 | 
			
		||||
                |> List.head
 | 
			
		||||
                |> Maybe.map .id
 | 
			
		||||
    in
 | 
			
		||||
    Util.Folder.isFolderMember model.allFolders selected
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,15 @@ module Comp.SourceManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
 | 
			
		||||
import Api.Model.SourceList exposing (SourceList)
 | 
			
		||||
import Api.Model.SourceTagIn exposing (SourceTagIn)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.SourceForm
 | 
			
		||||
import Comp.SourceTable exposing (SelectMode(..))
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
@@ -22,6 +24,7 @@ import Html.Events exposing (onClick, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Ports
 | 
			
		||||
import QRCode
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -408,3 +411,240 @@ viewForm flags settings model =
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 flags settings model =
 | 
			
		||||
    case model.viewMode of
 | 
			
		||||
        None ->
 | 
			
		||||
            viewTable2 model
 | 
			
		||||
 | 
			
		||||
        Edit _ ->
 | 
			
		||||
            div [] (viewForm2 flags settings model)
 | 
			
		||||
 | 
			
		||||
        Display source ->
 | 
			
		||||
            viewLinks2 flags settings source
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "relative flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start = []
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewSource
 | 
			
		||||
                    , title = "Add a source url"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New source"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.SourceTable.view2 model.sources)
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewLinks2 : Flags -> UiSettings -> SourceAndTags -> Html Msg
 | 
			
		||||
viewLinks2 flags _ source =
 | 
			
		||||
    let
 | 
			
		||||
        appUrl =
 | 
			
		||||
            flags.config.baseUrl ++ "/app/upload/" ++ source.source.id
 | 
			
		||||
 | 
			
		||||
        apiUrl =
 | 
			
		||||
            flags.config.baseUrl ++ "/api/v1/open/upload/item/" ++ source.source.id
 | 
			
		||||
 | 
			
		||||
        styleUrl =
 | 
			
		||||
            "truncate px-2 py-2 border-0 border-t border-b border-r font-mono text-sm my-auto rounded-r border-gray-400 dark:border-bluegray-500"
 | 
			
		||||
 | 
			
		||||
        styleQr =
 | 
			
		||||
            "max-w-min dark:bg-bluegray-400 bg-gray-50 mx-auto md:mx-0"
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        []
 | 
			
		||||
        [ h2 [ class S.header2 ]
 | 
			
		||||
            [ text "Public Uploads: "
 | 
			
		||||
            , text source.source.abbrev
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text source.source.id
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , p [ class "text-lg pt-2 opacity-75" ]
 | 
			
		||||
            [ text "This source defines URLs that can be used by anyone to send files to "
 | 
			
		||||
            , text "you. There is a web page that you can share or the API url can be used "
 | 
			
		||||
            , text "with other clients."
 | 
			
		||||
            ]
 | 
			
		||||
        , p [ class "text-lg py-2 opacity-75" ]
 | 
			
		||||
            [ text "There have been "
 | 
			
		||||
            , String.fromInt source.source.counter |> text
 | 
			
		||||
            , text " items created through this source."
 | 
			
		||||
            ]
 | 
			
		||||
        , h3
 | 
			
		||||
            [ class S.header3
 | 
			
		||||
            , class "mt-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Public Upload Page"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ div [ class "flex flex-row" ]
 | 
			
		||||
                [ a
 | 
			
		||||
                    [ class S.secondaryBasicButtonPlain
 | 
			
		||||
                    , class "rounded-l border text-sm px-4 py-2"
 | 
			
		||||
                    , title "Copy to clipboard"
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , Tuple.second appClipboardData
 | 
			
		||||
                        |> String.dropLeft 1
 | 
			
		||||
                        |> id
 | 
			
		||||
                    , attribute "data-clipboard-target" "#app-url"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-copy" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , a
 | 
			
		||||
                    [ class S.secondaryBasicButtonPlain
 | 
			
		||||
                    , class "px-4 py-2 border-0 border-t border-b border-r text-sm"
 | 
			
		||||
                    , href appUrl
 | 
			
		||||
                    , target "_blank"
 | 
			
		||||
                    , title "Open in new tab/window"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-external-link-alt" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , div
 | 
			
		||||
                    [ id "app-url"
 | 
			
		||||
                    , class styleUrl
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text appUrl
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "py-2" ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class S.border
 | 
			
		||||
                , class styleQr
 | 
			
		||||
                ]
 | 
			
		||||
                [ qrCodeView appUrl
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , h3
 | 
			
		||||
            [ class S.header3
 | 
			
		||||
            , class "mt-4"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Public API Upload URL"
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "" ]
 | 
			
		||||
            [ div [ class "flex flex-row" ]
 | 
			
		||||
                [ a
 | 
			
		||||
                    [ class S.secondaryBasicButtonPlain
 | 
			
		||||
                    , class "px-4 py-2 rounded-l border text-sm"
 | 
			
		||||
                    , title "Copy to clipboard"
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    , Tuple.second apiClipboardData
 | 
			
		||||
                        |> String.dropLeft 1
 | 
			
		||||
                        |> id
 | 
			
		||||
                    , attribute "data-clipboard-target" "#api-url"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-copy" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , div
 | 
			
		||||
                    [ class styleUrl
 | 
			
		||||
                    , id "api-url"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ text apiUrl
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "py-2" ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class S.border
 | 
			
		||||
                , class styleQr
 | 
			
		||||
                ]
 | 
			
		||||
                [ qrCodeView apiUrl
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , button
 | 
			
		||||
            [ class S.secondaryButton
 | 
			
		||||
            , class "mt-4 mb-2"
 | 
			
		||||
            , onClick SetTableView
 | 
			
		||||
            ]
 | 
			
		||||
            [ text "Back"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : Flags -> UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewForm2 flags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        newSource =
 | 
			
		||||
            model.formModel.source.source.id == ""
 | 
			
		||||
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this source?"
 | 
			
		||||
    in
 | 
			
		||||
    [ if newSource then
 | 
			
		||||
        h3 [ class S.header2 ]
 | 
			
		||||
            [ text "Create new source"
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
      else
 | 
			
		||||
        h3 [ class S.header2 ]
 | 
			
		||||
            [ text model.formModel.source.source.abbrev
 | 
			
		||||
            , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Id: "
 | 
			
		||||
                , text model.formModel.source.source.id
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
    , Html.form
 | 
			
		||||
        [ class "flex flex-col md:relative"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetTableView
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newSource then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this settings entry"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg
 | 
			
		||||
            (Comp.SourceForm.view2 flags settings model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.errorMessage, True )
 | 
			
		||||
                , ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,18 @@ module Comp.SourceTable exposing
 | 
			
		||||
    , isEdit
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Priority
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Html
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type SelectMode
 | 
			
		||||
@@ -48,6 +52,10 @@ update _ msg =
 | 
			
		||||
            ( Cmd.none, Display source )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : List SourceAndTags -> Html Msg
 | 
			
		||||
view sources =
 | 
			
		||||
    table [ class "ui table" ]
 | 
			
		||||
@@ -115,3 +123,63 @@ renderSourceLine source =
 | 
			
		||||
            [ text source.source.id
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : List SourceAndTags -> Html Msg
 | 
			
		||||
view2 sources =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Abbrev" ]
 | 
			
		||||
                , th [ class "px-2 text-center" ] [ text "Enabled" ]
 | 
			
		||||
                , th [ class "hidden md:table-cell" ] [ text "Counter" ]
 | 
			
		||||
                , th [ class "hidden md:table-cell" ] [ text "Priority" ]
 | 
			
		||||
                , th [ class "hidden sm:table-cell" ] [ text "Id" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map renderSourceLine2 sources)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderSourceLine2 : SourceAndTags -> Html Msg
 | 
			
		||||
renderSourceLine2 source =
 | 
			
		||||
    tr
 | 
			
		||||
        [ class S.tableRow ]
 | 
			
		||||
        [ td [ class S.editLinkTableCellStyle ]
 | 
			
		||||
            [ div
 | 
			
		||||
                [ class "inline-flex space-x-2"
 | 
			
		||||
                ]
 | 
			
		||||
                [ B.editLinkLabel (Select source)
 | 
			
		||||
                , B.linkLabel
 | 
			
		||||
                    { label = "Show"
 | 
			
		||||
                    , icon = "fa fa-eye"
 | 
			
		||||
                    , handler = Show source
 | 
			
		||||
                    , disabled = not source.source.enabled
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left" ]
 | 
			
		||||
            [ text source.source.abbrev
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "w-px px-2 text-center" ]
 | 
			
		||||
            [ Util.Html.checkbox2 source.source.enabled
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center hidden md:table-cell" ]
 | 
			
		||||
            [ source.source.counter |> String.fromInt |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center hidden md:table-cell" ]
 | 
			
		||||
            [ Data.Priority.fromString source.source.priority
 | 
			
		||||
                |> Maybe.map Data.Priority.toName
 | 
			
		||||
                |> Maybe.withDefault source.source.priority
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center hidden sm:table-cell" ]
 | 
			
		||||
            [ text source.source.id
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,13 @@ module Comp.StringListInput exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -96,3 +98,50 @@ view values model =
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : List String -> Model -> Html Msg
 | 
			
		||||
view2 values model =
 | 
			
		||||
    let
 | 
			
		||||
        valueItem s =
 | 
			
		||||
            div [ class "flex flex-row items-center" ]
 | 
			
		||||
                [ a
 | 
			
		||||
                    [ class S.deleteLabel
 | 
			
		||||
                    , onClick (RemoveString s)
 | 
			
		||||
                    , href "#"
 | 
			
		||||
                    ]
 | 
			
		||||
                    [ i [ class "fa fa-trash" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , span [ class "ml-2" ]
 | 
			
		||||
                    [ text s
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "relative" ]
 | 
			
		||||
            [ input
 | 
			
		||||
                [ placeholder ""
 | 
			
		||||
                , type_ "text"
 | 
			
		||||
                , onInput SetString
 | 
			
		||||
                , value model.currentInput
 | 
			
		||||
                , class ("pr-10 py-2 rounded" ++ S.textInput)
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , onClick AddString
 | 
			
		||||
                , class S.inputLeftIconLink
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "fa fa-plus" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "flex flex-col space-y-4 md:space-y-2 mt-2"
 | 
			
		||||
            , class "px-2 border-0 border-l dark:border-bluegray-600"
 | 
			
		||||
            ]
 | 
			
		||||
            (List.map valueItem values)
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										113
									
								
								modules/webapp/src/main/elm/Comp/Tabs.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								modules/webapp/src/main/elm/Comp/Tabs.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
module Comp.Tabs exposing
 | 
			
		||||
    ( State(..)
 | 
			
		||||
    , Style
 | 
			
		||||
    , Tab
 | 
			
		||||
    , akkordion
 | 
			
		||||
    , akkordionTab
 | 
			
		||||
    , defaultStyle
 | 
			
		||||
    , searchMenuStyle
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Tab msg =
 | 
			
		||||
    { title : String
 | 
			
		||||
    , info : Maybe String
 | 
			
		||||
    , body : List (Html msg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Style =
 | 
			
		||||
    { rootClasses : String
 | 
			
		||||
    , tabClasses : String
 | 
			
		||||
    , titleClasses : String
 | 
			
		||||
    , bodyClasses : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type State
 | 
			
		||||
    = Open
 | 
			
		||||
    | Closed
 | 
			
		||||
    | Hidden
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
defaultStyle : Style
 | 
			
		||||
defaultStyle =
 | 
			
		||||
    { rootClasses = "border-0 border-t  dark:border-bluegray-600"
 | 
			
		||||
    , tabClasses = "border-0 border-b  dark:border-bluegray-600"
 | 
			
		||||
    , titleClasses = "py-4 md:py-2 px-2 bg-gray-50 hover:bg-gray-100 dark:bg-bluegray-700 dark:bg-opacity-50 dark:hover:bg-opacity-100"
 | 
			
		||||
    , bodyClasses = "mt-2 py-2"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
searchMenuStyle : Style
 | 
			
		||||
searchMenuStyle =
 | 
			
		||||
    { rootClasses = "border-0 "
 | 
			
		||||
    , tabClasses = "border-0 "
 | 
			
		||||
    , titleClasses = "py-4 md:py-2 pl-2 bg-blue-50 hover:bg-blue-100 dark:bg-bluegray-700 dark:hover:bg-opacity-100 dark:hover:bg-bluegray-600 rounded"
 | 
			
		||||
    , bodyClasses = "mt-1 py-1 pl-2"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
akkordion : Style -> (Tab msg -> ( State, msg )) -> List (Tab msg) -> Html msg
 | 
			
		||||
akkordion style state tabs =
 | 
			
		||||
    let
 | 
			
		||||
        viewTab t =
 | 
			
		||||
            let
 | 
			
		||||
                ( open, m ) =
 | 
			
		||||
                    state t
 | 
			
		||||
            in
 | 
			
		||||
            akkordionTab style open m t
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class style.rootClasses
 | 
			
		||||
        , class "flex flex-col"
 | 
			
		||||
        ]
 | 
			
		||||
        (List.map viewTab tabs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
akkordionTab : Style -> State -> msg -> Tab msg -> Html msg
 | 
			
		||||
akkordionTab style state toggle tab =
 | 
			
		||||
    let
 | 
			
		||||
        tabTitle =
 | 
			
		||||
            a
 | 
			
		||||
                [ class "flex flex-row items-center"
 | 
			
		||||
                , class style.titleClasses
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick toggle
 | 
			
		||||
                ]
 | 
			
		||||
                [ div [ class "inline-flex mr-2 w-2" ]
 | 
			
		||||
                    [ if state == Open then
 | 
			
		||||
                        i [ class "fa fa-caret-down" ] []
 | 
			
		||||
 | 
			
		||||
                      else
 | 
			
		||||
                        i [ class "fa fa-caret-right" ] []
 | 
			
		||||
                    ]
 | 
			
		||||
                , div [ class "flex flex-col" ]
 | 
			
		||||
                    [ div [ class "px-2 font-semibold" ]
 | 
			
		||||
                        [ text tab.title
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div [ class "px-2 opacity-50 text-sm" ]
 | 
			
		||||
                        [ text (Maybe.withDefault "" tab.info)
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        tabContent =
 | 
			
		||||
            div
 | 
			
		||||
                [ classList [ ( "hidden", state == Closed ) ]
 | 
			
		||||
                , class style.bodyClasses
 | 
			
		||||
                ]
 | 
			
		||||
                tab.body
 | 
			
		||||
    in
 | 
			
		||||
    div
 | 
			
		||||
        [ class style.tabClasses
 | 
			
		||||
        , class "flex flex-col"
 | 
			
		||||
        , classList [ ( "hidden", state == Hidden ) ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ tabTitle
 | 
			
		||||
        , tabContent
 | 
			
		||||
        ]
 | 
			
		||||
@@ -6,14 +6,18 @@ module Comp.TagForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -137,3 +141,45 @@ view model =
 | 
			
		||||
            , Html.map CatMsg (Comp.Dropdown.viewSingle model.catDropdown)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ for "tagname"
 | 
			
		||||
                , class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Name"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , placeholder "Name"
 | 
			
		||||
                , value model.name
 | 
			
		||||
                , id "tagname"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList
 | 
			
		||||
                    [ ( S.inputErrorBorder
 | 
			
		||||
                      , not (isValid model)
 | 
			
		||||
                      )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label
 | 
			
		||||
                [ class S.inputLabel
 | 
			
		||||
                ]
 | 
			
		||||
                [ text "Category"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map CatMsg
 | 
			
		||||
                (Comp.Dropdown.viewSingle2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    model.catDropdown
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,15 @@ module Comp.TagManage exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.Tag
 | 
			
		||||
import Api.Model.TagList exposing (TagList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.TagForm
 | 
			
		||||
import Comp.TagTable
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
@@ -18,6 +21,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Tag
 | 
			
		||||
@@ -52,6 +56,11 @@ emptyModel =
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dimmerSettings : Comp.YesNoDimmer.Settings
 | 
			
		||||
dimmerSettings =
 | 
			
		||||
    Comp.YesNoDimmer.defaultSettings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
    = TableMsg Comp.TagTable.Msg
 | 
			
		||||
    | FormMsg Comp.TagForm.Msg
 | 
			
		||||
@@ -309,3 +318,120 @@ viewForm model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
        viewTable2 model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewForm2 model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.TextInput
 | 
			
		||||
                    { tagger = SetQuery
 | 
			
		||||
                    , value = model.query
 | 
			
		||||
                    , placeholder = "Search…"
 | 
			
		||||
                    , icon = Just "fa fa-search"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewTag
 | 
			
		||||
                    , title = "Create a new tag"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New Tag"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.TagTable.view2 model.tagTableModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "ui dimmer", True )
 | 
			
		||||
                , ( "active", model.loading )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : Model -> Html Msg
 | 
			
		||||
viewForm2 model =
 | 
			
		||||
    let
 | 
			
		||||
        newTag =
 | 
			
		||||
            model.tagFormModel.tag.id == ""
 | 
			
		||||
 | 
			
		||||
        dimmerSettings2 =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this tag?"
 | 
			
		||||
    in
 | 
			
		||||
    Html.form
 | 
			
		||||
        [ class "relative flex flex-col"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN
 | 
			
		||||
                True
 | 
			
		||||
                dimmerSettings2
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , if newTag then
 | 
			
		||||
            h1 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new tag"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h1 [ class S.header2 ]
 | 
			
		||||
                [ text model.tagFormModel.tag.name
 | 
			
		||||
                , div [ class "opacity-50 text-sm" ]
 | 
			
		||||
                    [ text "Id: "
 | 
			
		||||
                    , text model.tagFormModel.tag.id
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newTag then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this tag"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg (Comp.TagForm.view2 model.tagFormModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,10 @@ module Comp.TagSelect exposing
 | 
			
		||||
    , Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , Selection
 | 
			
		||||
    , WorkModel
 | 
			
		||||
    , emptySelection
 | 
			
		||||
    , init
 | 
			
		||||
    , makeWorkModel
 | 
			
		||||
    , modifyAll
 | 
			
		||||
    , modifyCount
 | 
			
		||||
    , reset
 | 
			
		||||
@@ -12,6 +14,9 @@ module Comp.TagSelect exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , updateDrop
 | 
			
		||||
    , viewAll
 | 
			
		||||
    , viewAll2
 | 
			
		||||
    , viewCats2
 | 
			
		||||
    , viewTagsDrop2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
@@ -25,6 +30,7 @@ import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Set
 | 
			
		||||
import Simple.Fuzzy
 | 
			
		||||
import String as S
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.ExpandCollapse
 | 
			
		||||
import Util.ItemDragDrop as DD
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
@@ -413,6 +419,10 @@ catState model name =
 | 
			
		||||
            Deselect
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewAll : DD.Model -> UiSettings -> Selection -> Model -> List (Html Msg)
 | 
			
		||||
viewAll ddm settings sel model =
 | 
			
		||||
    let
 | 
			
		||||
@@ -630,3 +640,219 @@ getIcon state color default =
 | 
			
		||||
 | 
			
		||||
        Deselect ->
 | 
			
		||||
            default color
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewAll2 : DD.Model -> UiSettings -> Selection -> Model -> List (Html Msg)
 | 
			
		||||
viewAll2 ddm settings sel model =
 | 
			
		||||
    let
 | 
			
		||||
        wm =
 | 
			
		||||
            makeWorkModel sel model
 | 
			
		||||
    in
 | 
			
		||||
    viewTagsDrop2 ddm wm settings model ++ [ viewCats2 settings wm model ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTagsDrop2 : DD.Model -> WorkModel -> UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewTagsDrop2 ddm wm settings model =
 | 
			
		||||
    [ div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "flex flex-row h-6 items-center text-xs mb-2" ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ class S.secondaryBasicButtonPlain
 | 
			
		||||
                , class "border rounded flex-none px-1 py-1"
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick ToggleShowEmpty
 | 
			
		||||
                ]
 | 
			
		||||
                [ if model.showEmpty then
 | 
			
		||||
                    text " Hide empty"
 | 
			
		||||
 | 
			
		||||
                  else
 | 
			
		||||
                    text " Show empty"
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "flex-grow" ] []
 | 
			
		||||
            , div [ class " relative h-6" ]
 | 
			
		||||
                [ input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , placeholder "Filter …"
 | 
			
		||||
                    , onInput Search
 | 
			
		||||
                    , class "bg-blue-50 w-30 h-6 px-0 py-0 text-xs"
 | 
			
		||||
                    , class "border-0 border-b border-gray-200 focus:ring-0 focus:border-black"
 | 
			
		||||
                    , class "dark:bg-bluegray-700 dark:text-bluegray-200 dark:border-bluegray-400 dark:focus:border-white"
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                , i [ class "fa fa-search absolute top-1/3 right-0 opacity-50" ] []
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , div [ class "flex flex-col space-y-2 md:space-y-1" ]
 | 
			
		||||
        (renderTagItems2 ddm settings model wm)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCats2 : UiSettings -> WorkModel -> Model -> Html Msg
 | 
			
		||||
viewCats2 settings wm model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div [ class "flex flex-col space-y-2 md:space-y-1" ]
 | 
			
		||||
            (renderCatItems2 settings model wm)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderTagItems2 : DD.Model -> UiSettings -> Model -> WorkModel -> List (Html Msg)
 | 
			
		||||
renderTagItems2 ddm settings model wm =
 | 
			
		||||
    let
 | 
			
		||||
        tags =
 | 
			
		||||
            wm.filteredTags
 | 
			
		||||
 | 
			
		||||
        max =
 | 
			
		||||
            settings.searchMenuTagCount
 | 
			
		||||
 | 
			
		||||
        expLink =
 | 
			
		||||
            Util.ExpandCollapse.expandToggle2
 | 
			
		||||
                max
 | 
			
		||||
                (List.length tags)
 | 
			
		||||
                ToggleExpandTags
 | 
			
		||||
 | 
			
		||||
        cpsLink =
 | 
			
		||||
            Util.ExpandCollapse.collapseToggle2
 | 
			
		||||
                max
 | 
			
		||||
                (List.length tags)
 | 
			
		||||
                ToggleExpandTags
 | 
			
		||||
    in
 | 
			
		||||
    if max <= 0 then
 | 
			
		||||
        List.map (viewTagItem2 ddm settings wm) tags
 | 
			
		||||
 | 
			
		||||
    else if model.expandedTags then
 | 
			
		||||
        List.map (viewTagItem2 ddm settings wm) tags ++ cpsLink
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.map (viewTagItem2 ddm settings wm) (List.take max tags) ++ expLink
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTagItem2 : DD.Model -> UiSettings -> WorkModel -> TagCount -> Html Msg
 | 
			
		||||
viewTagItem2 ddm settings model tag =
 | 
			
		||||
    let
 | 
			
		||||
        state =
 | 
			
		||||
            tagState model tag.tag.id
 | 
			
		||||
 | 
			
		||||
        color =
 | 
			
		||||
            Data.UiSettings.tagColorFg2 tag.tag settings
 | 
			
		||||
 | 
			
		||||
        icon =
 | 
			
		||||
            getIcon2 state color I.tagIcon2
 | 
			
		||||
 | 
			
		||||
        dropActive =
 | 
			
		||||
            DD.getDropId ddm == Just (DD.Tag tag.tag.id)
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        ([ classList
 | 
			
		||||
            [ ( "bg-blue-100 dark:bg-bluegray-600", dropActive )
 | 
			
		||||
            ]
 | 
			
		||||
         , class "flex flex-row items-center"
 | 
			
		||||
         , class "rounded px-1 py-1 hover:bg-blue-100 dark:hover:bg-bluegray-600"
 | 
			
		||||
         , href "#"
 | 
			
		||||
         , onClick (ToggleTag tag.tag.id)
 | 
			
		||||
         ]
 | 
			
		||||
            ++ DD.droppable TagDDMsg (DD.Tag tag.tag.id)
 | 
			
		||||
        )
 | 
			
		||||
        [ icon
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "font-semibold", state == Include )
 | 
			
		||||
                , ( "", state /= Include )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "ml-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text tag.tag.name
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , numberLabel tag.count
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCategoryItem2 : UiSettings -> WorkModel -> Category -> Html Msg
 | 
			
		||||
viewCategoryItem2 settings model cat =
 | 
			
		||||
    let
 | 
			
		||||
        state =
 | 
			
		||||
            catState model cat.name
 | 
			
		||||
 | 
			
		||||
        color =
 | 
			
		||||
            Data.UiSettings.catColorFg2 settings cat.name
 | 
			
		||||
 | 
			
		||||
        icon =
 | 
			
		||||
            getIcon2 state color I.tagsIcon2
 | 
			
		||||
    in
 | 
			
		||||
    a
 | 
			
		||||
        [ class "flex flex-row items-center"
 | 
			
		||||
        , class "rounded px-1 py-1 hover:bg-blue-100 dark:hover:bg-bluegray-600"
 | 
			
		||||
        , href "#"
 | 
			
		||||
        , onClick (ToggleCat cat.name)
 | 
			
		||||
        ]
 | 
			
		||||
        [ icon
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "font-semibold", state == Include )
 | 
			
		||||
                , ( "", state /= Include )
 | 
			
		||||
                ]
 | 
			
		||||
            , class "ml-2"
 | 
			
		||||
            ]
 | 
			
		||||
            [ text cat.name
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex-grow" ] []
 | 
			
		||||
        , numberLabel cat.count
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderCatItems2 : UiSettings -> Model -> WorkModel -> List (Html Msg)
 | 
			
		||||
renderCatItems2 settings model wm =
 | 
			
		||||
    let
 | 
			
		||||
        cats =
 | 
			
		||||
            wm.filteredCats
 | 
			
		||||
 | 
			
		||||
        max =
 | 
			
		||||
            settings.searchMenuTagCatCount
 | 
			
		||||
 | 
			
		||||
        expLink =
 | 
			
		||||
            Util.ExpandCollapse.expandToggle2
 | 
			
		||||
                max
 | 
			
		||||
                (List.length cats)
 | 
			
		||||
                ToggleExpandCats
 | 
			
		||||
 | 
			
		||||
        cpsLink =
 | 
			
		||||
            Util.ExpandCollapse.collapseToggle2
 | 
			
		||||
                max
 | 
			
		||||
                (List.length cats)
 | 
			
		||||
                ToggleExpandCats
 | 
			
		||||
    in
 | 
			
		||||
    if max <= 0 then
 | 
			
		||||
        List.map (viewCategoryItem2 settings wm) cats
 | 
			
		||||
 | 
			
		||||
    else if model.expandedCats then
 | 
			
		||||
        List.map (viewCategoryItem2 settings wm) cats ++ cpsLink
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        List.map (viewCategoryItem2 settings wm) (List.take max cats) ++ expLink
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
getIcon2 : SelState -> String -> (String -> Html msg) -> Html msg
 | 
			
		||||
getIcon2 state color default =
 | 
			
		||||
    case state of
 | 
			
		||||
        Include ->
 | 
			
		||||
            i [ class ("fa fa-check " ++ color) ] []
 | 
			
		||||
 | 
			
		||||
        Exclude ->
 | 
			
		||||
            i [ class ("fa fa-minus " ++ color) ] []
 | 
			
		||||
 | 
			
		||||
        Deselect ->
 | 
			
		||||
            default color
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
numberLabel : Int -> Html msg
 | 
			
		||||
numberLabel num =
 | 
			
		||||
    div
 | 
			
		||||
        [ class "bg-gray-200 border rounded-full h-6 w-6 flex items-center justify-center text-xs"
 | 
			
		||||
        , class "dark:bg-bluegray-800 dark:text-bluegray-200 dark:border-bluegray-800 dark:bg-opacity-50"
 | 
			
		||||
        ]
 | 
			
		||||
        [ text (String.fromInt num)
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,16 @@ module Comp.TagTable exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -63,8 +66,7 @@ view model =
 | 
			
		||||
renderTagLine : Model -> Tag -> Html Msg
 | 
			
		||||
renderTagLine model tag =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just tag ) ]
 | 
			
		||||
        ]
 | 
			
		||||
        []
 | 
			
		||||
        [ td [ class "collapsing" ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
@@ -82,3 +84,38 @@ renderTagLine model tag =
 | 
			
		||||
            [ Maybe.withDefault "-" tag.category |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Name" ]
 | 
			
		||||
                , th [ class "text-left" ] [ text "Category" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderTagLine2 model) model.tags)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderTagLine2 : Model -> Tag -> Html Msg
 | 
			
		||||
renderTagLine2 model tag =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just tag ) ]
 | 
			
		||||
        , class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (Select tag)
 | 
			
		||||
        , td [ class "text-left py-4 md:py-2" ]
 | 
			
		||||
            [ text tag.name
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-left py-4 md:py-2" ]
 | 
			
		||||
            [ Maybe.withDefault "-" tag.category |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ module Comp.UiSettingsForm exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
@@ -12,6 +13,8 @@ import Comp.BasicSizeField
 | 
			
		||||
import Comp.ColorTagger
 | 
			
		||||
import Comp.FieldListSelect
 | 
			
		||||
import Comp.IntField
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.Tabs
 | 
			
		||||
import Data.BasicSize exposing (BasicSize)
 | 
			
		||||
import Data.Color exposing (Color)
 | 
			
		||||
import Data.Fields exposing (Field)
 | 
			
		||||
@@ -24,6 +27,8 @@ import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onCheck, onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Markdown
 | 
			
		||||
import Set exposing (Set)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
import Util.Tag
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +57,8 @@ type alias Model =
 | 
			
		||||
    , cardSubtitlePattern : PatternModel
 | 
			
		||||
    , showPatternHelp : Bool
 | 
			
		||||
    , searchStatsVisible : Bool
 | 
			
		||||
    , sideMenuVisible : Bool
 | 
			
		||||
    , openTabs : Set String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -143,6 +150,8 @@ init flags settings =
 | 
			
		||||
      , cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
 | 
			
		||||
      , showPatternHelp = False
 | 
			
		||||
      , searchStatsVisible = settings.searchStatsVisible
 | 
			
		||||
      , sideMenuVisible = settings.sideMenuVisible
 | 
			
		||||
      , openTabs = Set.empty
 | 
			
		||||
      }
 | 
			
		||||
    , Api.getTags flags "" GetTagsResp
 | 
			
		||||
    )
 | 
			
		||||
@@ -167,6 +176,8 @@ type Msg
 | 
			
		||||
    | SetCardSubtitlePattern String
 | 
			
		||||
    | TogglePatternHelpMsg
 | 
			
		||||
    | ToggleSearchStatsVisible
 | 
			
		||||
    | ToggleAkkordionTab String
 | 
			
		||||
    | ToggleSideMenuVisible
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -427,6 +438,28 @@ update sett msg model =
 | 
			
		||||
            , Just { sett | searchStatsVisible = flag }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        ToggleAkkordionTab title ->
 | 
			
		||||
            let
 | 
			
		||||
                tabs =
 | 
			
		||||
                    if Set.member title model.openTabs then
 | 
			
		||||
                        Set.remove title model.openTabs
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Set.insert title model.openTabs
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | openTabs = tabs }
 | 
			
		||||
            , Nothing
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        ToggleSideMenuVisible ->
 | 
			
		||||
            let
 | 
			
		||||
                next =
 | 
			
		||||
                    not model.sideMenuVisible
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | sideMenuVisible = next }
 | 
			
		||||
            , Just { sett | sideMenuVisible = next }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
@@ -655,3 +688,231 @@ view flags _ model =
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map FieldListMsg (Comp.FieldListSelect.view model.formFields)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
tagColorViewOpts2 : Comp.ColorTagger.ViewOpts
 | 
			
		||||
tagColorViewOpts2 =
 | 
			
		||||
    { renderItem =
 | 
			
		||||
        \( k, v ) ->
 | 
			
		||||
            span [ class (" label " ++ Data.Color.toString2 v) ]
 | 
			
		||||
                [ text k ]
 | 
			
		||||
    , label = "Choose color for tag categories"
 | 
			
		||||
    , description = Just "Tags can be represented differently based on their category."
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 flags settings model =
 | 
			
		||||
    let
 | 
			
		||||
        state tab =
 | 
			
		||||
            if Set.member tab.title model.openTabs then
 | 
			
		||||
                Comp.Tabs.Open
 | 
			
		||||
 | 
			
		||||
            else
 | 
			
		||||
                Comp.Tabs.Closed
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ Comp.Tabs.akkordion
 | 
			
		||||
            Comp.Tabs.defaultStyle
 | 
			
		||||
            (\t -> ( state t, ToggleAkkordionTab t.title ))
 | 
			
		||||
            (settingFormTabs flags settings model)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
settingFormTabs : Flags -> UiSettings -> Model -> List (Comp.Tabs.Tab Msg)
 | 
			
		||||
settingFormTabs flags _ model =
 | 
			
		||||
    [ { title = "General"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4 " ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { id = "uisetting-sidemenu-visible"
 | 
			
		||||
                        , label = "Show side menu by default"
 | 
			
		||||
                        , tagger = \_ -> ToggleSideMenuVisible
 | 
			
		||||
                        , value = model.sideMenuVisible
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Item Search"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map SearchPageSizeMsg
 | 
			
		||||
                (Comp.IntField.viewWithInfo2
 | 
			
		||||
                    ("Maximum results in one page when searching items. At most "
 | 
			
		||||
                        ++ String.fromInt flags.config.maxPageSize
 | 
			
		||||
                        ++ "."
 | 
			
		||||
                    )
 | 
			
		||||
                    model.itemSearchPageSize
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    model.searchPageSizeModel
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "mb-4" ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { id = "uisetting-searchstats-visible"
 | 
			
		||||
                        , value = model.searchStatsVisible
 | 
			
		||||
                        , tagger = \_ -> ToggleSearchStatsVisible
 | 
			
		||||
                        , label = "Show basic search statistics by default"
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Item Cards"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map NoteLengthMsg
 | 
			
		||||
                (Comp.IntField.viewWithInfo2
 | 
			
		||||
                    ("Maximum size of the item notes to display in card view. Between 0 - "
 | 
			
		||||
                        ++ String.fromInt flags.config.maxNoteLength
 | 
			
		||||
                        ++ "."
 | 
			
		||||
                    )
 | 
			
		||||
                    model.itemSearchNoteLength
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    model.searchNoteLengthModel
 | 
			
		||||
                )
 | 
			
		||||
            , Html.map CardPreviewSizeMsg
 | 
			
		||||
                (Comp.BasicSizeField.view2
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    "Size of item preview"
 | 
			
		||||
                    model.cardPreviewSize
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "mb-4" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Card Title Pattern"
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ class "float-right"
 | 
			
		||||
                        , class S.link
 | 
			
		||||
                        , title "Toggle pattern help text"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , onClick TogglePatternHelpMsg
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-question" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , Maybe.withDefault "" model.cardTitlePattern.pattern |> value
 | 
			
		||||
                    , onInput SetCardTitlePattern
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mb-4" ]
 | 
			
		||||
                [ label [ class S.inputLabel ]
 | 
			
		||||
                    [ text "Card Subtitle Pattern"
 | 
			
		||||
                    , a
 | 
			
		||||
                        [ class "float-right"
 | 
			
		||||
                        , class S.link
 | 
			
		||||
                        , title "Toggle pattern help text"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , onClick TogglePatternHelpMsg
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "fa fa-question" ] []
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                , input
 | 
			
		||||
                    [ type_ "text"
 | 
			
		||||
                    , Maybe.withDefault "" model.cardSubtitlePattern.pattern |> value
 | 
			
		||||
                    , onInput SetCardSubtitlePattern
 | 
			
		||||
                    , class S.textInput
 | 
			
		||||
                    ]
 | 
			
		||||
                    []
 | 
			
		||||
                ]
 | 
			
		||||
            , Markdown.toHtml
 | 
			
		||||
                [ classList
 | 
			
		||||
                    [ ( S.message, True )
 | 
			
		||||
                    , ( "hidden", not model.showPatternHelp )
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
                IT.helpMessage
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Search Menu"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map SearchMenuTagMsg
 | 
			
		||||
                (Comp.IntField.viewWithInfo2
 | 
			
		||||
                    "How many tags to display in search menu at once. Others can be expanded. Use 0 to always show all."
 | 
			
		||||
                    model.searchMenuTagCount
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    model.searchMenuTagCountModel
 | 
			
		||||
                )
 | 
			
		||||
            , Html.map SearchMenuTagCatMsg
 | 
			
		||||
                (Comp.IntField.viewWithInfo2
 | 
			
		||||
                    "How many categories to display in search menu at once. Others can be expanded. Use 0 to always show all."
 | 
			
		||||
                    model.searchMenuTagCatCount
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    model.searchMenuTagCatCountModel
 | 
			
		||||
                )
 | 
			
		||||
            , Html.map SearchMenuFolderMsg
 | 
			
		||||
                (Comp.IntField.viewWithInfo2
 | 
			
		||||
                    "How many folders to display in search menu at once. Other folders can be expanded. Use 0 to always show all."
 | 
			
		||||
                    model.searchMenuFolderCount
 | 
			
		||||
                    "mb-4"
 | 
			
		||||
                    model.searchMenuFolderCountModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Item Detail"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ div [ class "mb-4" ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { tagger = \_ -> TogglePdfPreview
 | 
			
		||||
                        , label = "Browser-native PDF preview"
 | 
			
		||||
                        , value = model.nativePdfPreview
 | 
			
		||||
                        , id = "uisetting-pdfpreview-toggle"
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mb-4" ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { tagger = \_ -> ToggleItemDetailShortcuts
 | 
			
		||||
                        , label = "Use keyboard shortcuts for navigation and confirm/unconfirm with open edit menu."
 | 
			
		||||
                        , value = model.itemDetailShortcuts
 | 
			
		||||
                        , id = "uisetting-itemdetailshortcuts-toggle"
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "mb-4 hidden" ]
 | 
			
		||||
                [ MB.viewItem <|
 | 
			
		||||
                    MB.Checkbox
 | 
			
		||||
                        { id = "uisetting-editmenu-visible"
 | 
			
		||||
                        , value = model.editMenuVisible
 | 
			
		||||
                        , tagger = \_ -> ToggleEditMenuVisible
 | 
			
		||||
                        , label = "Show edit side menu by default"
 | 
			
		||||
                        }
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Tag Category Colors"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ Html.map TagColorMsg
 | 
			
		||||
                (Comp.ColorTagger.view2
 | 
			
		||||
                    model.tagColors
 | 
			
		||||
                    tagColorViewOpts2
 | 
			
		||||
                    model.tagColorModel
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    , { title = "Fields"
 | 
			
		||||
      , info = Nothing
 | 
			
		||||
      , body =
 | 
			
		||||
            [ span [ class "opacity-50 text-sm" ]
 | 
			
		||||
                [ text "Choose which fields to display in search and edit menus."
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map FieldListMsg
 | 
			
		||||
                (Comp.FieldListSelect.view2
 | 
			
		||||
                    "px-2"
 | 
			
		||||
                    model.formFields
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,11 @@ module Comp.UiSettingsManage exposing
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.UiSettingsForm
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
@@ -14,6 +16,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Ports
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -147,3 +150,42 @@ view flags settings classes model =
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Flags -> UiSettings -> String -> Model -> Html Msg
 | 
			
		||||
view2 flags settings classes model =
 | 
			
		||||
    div [ class classes ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    , title = "Save settings"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end = []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( S.successMessage, isSuccess model )
 | 
			
		||||
                , ( S.errorMessage, isError model )
 | 
			
		||||
                , ( "hidden", model.message == Nothing )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.message
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map UiSettingsFormMsg
 | 
			
		||||
            (Comp.UiSettingsForm.view2
 | 
			
		||||
                flags
 | 
			
		||||
                settings
 | 
			
		||||
                model.formModel
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,16 +7,20 @@ module Comp.UserForm exposing
 | 
			
		||||
    , isValid
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.User exposing (User)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.Dropdown
 | 
			
		||||
import Data.DropdownStyle as DS
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.UiSettings exposing (UiSettings)
 | 
			
		||||
import Data.UserState exposing (UserState)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onInput)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -159,6 +163,10 @@ update _ msg model =
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    div [ class "ui form" ]
 | 
			
		||||
@@ -208,3 +216,71 @@ view settings model =
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ div
 | 
			
		||||
            [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Login"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetLogin
 | 
			
		||||
                , placeholder "Login"
 | 
			
		||||
                , value model.login
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, model.login == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "E-Mail"
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ onInput SetEmail
 | 
			
		||||
                , type_ "text"
 | 
			
		||||
                , model.email |> Maybe.withDefault "" |> value
 | 
			
		||||
                , placeholder "E-Mail"
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "mb-4" ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "State"
 | 
			
		||||
                ]
 | 
			
		||||
            , Html.map StateMsg
 | 
			
		||||
                (Comp.Dropdown.view2
 | 
			
		||||
                    DS.mainStyle
 | 
			
		||||
                    settings
 | 
			
		||||
                    model.state
 | 
			
		||||
                )
 | 
			
		||||
            ]
 | 
			
		||||
        , div
 | 
			
		||||
            [ class "mb-4"
 | 
			
		||||
            , classList [ ( "hidden", model.user.login /= "" ) ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ label [ class S.inputLabel ]
 | 
			
		||||
                [ text "Password"
 | 
			
		||||
                , B.inputRequired
 | 
			
		||||
                ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetPassword
 | 
			
		||||
                , placeholder "Password"
 | 
			
		||||
                , Maybe.withDefault "" model.password |> value
 | 
			
		||||
                , class S.textInput
 | 
			
		||||
                , classList [ ( S.inputErrorBorder, Util.Maybe.isEmpty model.password ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,15 @@ module Comp.UserManage exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.User
 | 
			
		||||
import Api.Model.UserList exposing (UserList)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Comp.MenuBar as MB
 | 
			
		||||
import Comp.UserForm
 | 
			
		||||
import Comp.UserTable
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
@@ -19,6 +22,7 @@ import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onSubmit)
 | 
			
		||||
import Http
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
@@ -195,6 +199,10 @@ update flags msg model =
 | 
			
		||||
            ( { model | deleteConfirm = cm }, cmd )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : UiSettings -> Model -> Html Msg
 | 
			
		||||
view settings model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
@@ -271,3 +279,106 @@ viewForm settings model =
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
view2 settings model =
 | 
			
		||||
    if model.viewMode == Table then
 | 
			
		||||
        viewTable2 model
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        viewForm2 settings model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable2 : Model -> Html Msg
 | 
			
		||||
viewTable2 model =
 | 
			
		||||
    div [ class "flex flex-col" ]
 | 
			
		||||
        [ MB.view
 | 
			
		||||
            { start = []
 | 
			
		||||
            , end =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = InitNewUser
 | 
			
		||||
                    , title = "Add a new user"
 | 
			
		||||
                    , icon = Just "fa fa-plus"
 | 
			
		||||
                    , label = "New user"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map TableMsg (Comp.UserTable.view2 model.tableModel)
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewForm2 : UiSettings -> Model -> Html Msg
 | 
			
		||||
viewForm2 settings model =
 | 
			
		||||
    let
 | 
			
		||||
        newUser =
 | 
			
		||||
            Comp.UserForm.isNewUser model.formModel
 | 
			
		||||
 | 
			
		||||
        dimmerSettings : Comp.YesNoDimmer.Settings
 | 
			
		||||
        dimmerSettings =
 | 
			
		||||
            Comp.YesNoDimmer.defaultSettings2 "Really delete this user?"
 | 
			
		||||
    in
 | 
			
		||||
    Html.form
 | 
			
		||||
        [ class "flex flex-col md:relative"
 | 
			
		||||
        , onSubmit Submit
 | 
			
		||||
        ]
 | 
			
		||||
        [ Html.map YesNoMsg
 | 
			
		||||
            (Comp.YesNoDimmer.viewN True
 | 
			
		||||
                dimmerSettings
 | 
			
		||||
                model.deleteConfirm
 | 
			
		||||
            )
 | 
			
		||||
        , if newUser then
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text "Create new user"
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
          else
 | 
			
		||||
            h3 [ class S.header2 ]
 | 
			
		||||
                [ text model.formModel.user.login
 | 
			
		||||
                ]
 | 
			
		||||
        , MB.view
 | 
			
		||||
            { start =
 | 
			
		||||
                [ MB.PrimaryButton
 | 
			
		||||
                    { tagger = Submit
 | 
			
		||||
                    , title = "Submit this form"
 | 
			
		||||
                    , icon = Just "fa fa-save"
 | 
			
		||||
                    , label = "Submit"
 | 
			
		||||
                    }
 | 
			
		||||
                , MB.SecondaryButton
 | 
			
		||||
                    { tagger = SetViewMode Table
 | 
			
		||||
                    , title = "Back to list"
 | 
			
		||||
                    , icon = Just "fa fa-arrow-left"
 | 
			
		||||
                    , label = "Cancel"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            , end =
 | 
			
		||||
                if not newUser then
 | 
			
		||||
                    [ MB.DeleteButton
 | 
			
		||||
                        { tagger = RequestDelete
 | 
			
		||||
                        , title = "Delete this user"
 | 
			
		||||
                        , icon = Just "fa fa-trash"
 | 
			
		||||
                        , label = "Delete"
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    []
 | 
			
		||||
            , rootClasses = "mb-4"
 | 
			
		||||
            }
 | 
			
		||||
        , Html.map FormMsg (Comp.UserForm.view2 settings model.formModel)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "hidden", Util.Maybe.isEmpty model.formError )
 | 
			
		||||
                ]
 | 
			
		||||
            , class S.errorMessage
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.withDefault "" model.formError |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , B.loadingDimmer model.loading
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,16 @@ module Comp.UserTable exposing
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.User exposing (User)
 | 
			
		||||
import Comp.Basic as B
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
import Util.Time exposing (formatDateTime)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +49,10 @@ update _ msg model =
 | 
			
		||||
            ( { model | selected = Nothing }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> Html Msg
 | 
			
		||||
view model =
 | 
			
		||||
    table [ class "ui selectable table" ]
 | 
			
		||||
@@ -89,3 +96,54 @@ renderUserLine model user =
 | 
			
		||||
            [ formatDateTime user.created |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view2 : Model -> Html Msg
 | 
			
		||||
view2 model =
 | 
			
		||||
    table [ class S.tableMain ]
 | 
			
		||||
        [ thead []
 | 
			
		||||
            [ tr []
 | 
			
		||||
                [ th [ class "w-px whitespace-nowrap" ] []
 | 
			
		||||
                , th [ class "text-left" ] [ text "Login" ]
 | 
			
		||||
                , th [ class "text-center" ] [ text "State" ]
 | 
			
		||||
                , th [ class "hidden md:table-cell text-left" ] [ text "Email" ]
 | 
			
		||||
                , th [ class "hidden md:table-cell text-center" ] [ text "Logins" ]
 | 
			
		||||
                , th [ class "hidden sm:table-cell text-center" ] [ text "Last Login" ]
 | 
			
		||||
                , th [ class "hidden md:table-cell text-center" ] [ text "Created" ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , tbody []
 | 
			
		||||
            (List.map (renderUserLine2 model) model.users)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
renderUserLine2 : Model -> User -> Html Msg
 | 
			
		||||
renderUserLine2 model user =
 | 
			
		||||
    tr
 | 
			
		||||
        [ classList [ ( "active", model.selected == Just user ) ]
 | 
			
		||||
        , class S.tableRow
 | 
			
		||||
        ]
 | 
			
		||||
        [ B.editLinkTableCell (Select user)
 | 
			
		||||
        , td [ class "text-left" ]
 | 
			
		||||
            [ text user.login
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "text-center" ]
 | 
			
		||||
            [ text user.state
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden md:table-cell text-left" ]
 | 
			
		||||
            [ Maybe.withDefault "" user.email |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden md:table-cell text-center" ]
 | 
			
		||||
            [ String.fromInt user.loginCount |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden sm:table-cell text-center" ]
 | 
			
		||||
            [ Maybe.map formatDateTime user.lastLogin |> Maybe.withDefault "" |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td [ class "hidden md:table-cell text-center" ]
 | 
			
		||||
            [ formatDateTime user.created |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ module Comp.YesNoDimmer exposing
 | 
			
		||||
    , Settings
 | 
			
		||||
    , activate
 | 
			
		||||
    , defaultSettings
 | 
			
		||||
    , defaultSettings2
 | 
			
		||||
    , disable
 | 
			
		||||
    , emptyModel
 | 
			
		||||
    , initActive
 | 
			
		||||
@@ -11,11 +12,13 @@ module Comp.YesNoDimmer exposing
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    , view2
 | 
			
		||||
    , viewN
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Styles as S
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
@@ -53,7 +56,6 @@ type alias Settings =
 | 
			
		||||
    , headerClass : String
 | 
			
		||||
    , confirmButton : String
 | 
			
		||||
    , cancelButton : String
 | 
			
		||||
    , invertedDimmer : Bool
 | 
			
		||||
    , extraClass : String
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +67,17 @@ defaultSettings =
 | 
			
		||||
    , headerClass = "ui inverted icon header"
 | 
			
		||||
    , confirmButton = "Yes, do it!"
 | 
			
		||||
    , cancelButton = "No"
 | 
			
		||||
    , invertedDimmer = False
 | 
			
		||||
    , extraClass = ""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
defaultSettings2 : String -> Settings
 | 
			
		||||
defaultSettings2 msg =
 | 
			
		||||
    { message = msg
 | 
			
		||||
    , headerIcon = "fa fa-exclamation-circle mr-3"
 | 
			
		||||
    , headerClass = "text-2xl font-bold text-center w-full"
 | 
			
		||||
    , confirmButton = "Yes, do it!"
 | 
			
		||||
    , cancelButton = "No"
 | 
			
		||||
    , extraClass = ""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -104,7 +116,6 @@ view2 active settings model =
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "ui dimmer", True )
 | 
			
		||||
            , ( settings.extraClass, True )
 | 
			
		||||
            , ( "inverted", settings.invertedDimmer )
 | 
			
		||||
            , ( "active", active && model.active )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
@@ -130,3 +141,42 @@ view2 active settings model =
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewN : Bool -> Settings -> Model -> Html Msg
 | 
			
		||||
viewN active settings model =
 | 
			
		||||
    div
 | 
			
		||||
        [ class S.dimmer
 | 
			
		||||
        , class settings.extraClass
 | 
			
		||||
        , classList
 | 
			
		||||
            [ ( "hidden", not active || not model.active )
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        [ div [ class settings.headerClass ]
 | 
			
		||||
            [ i
 | 
			
		||||
                [ class settings.headerIcon
 | 
			
		||||
                , class "text-gray-200 font-semibold"
 | 
			
		||||
                , classList [ ( "hidden", settings.headerClass == "" ) ]
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , span [ class "text-gray-200 font-semibold" ]
 | 
			
		||||
                [ text settings.message
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , div [ class "flex flex-row space-x-2 text-xs mt-2" ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ class (S.primaryButton ++ "block font-semibold")
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick ConfirmDelete
 | 
			
		||||
                ]
 | 
			
		||||
                [ text settings.confirmButton
 | 
			
		||||
                ]
 | 
			
		||||
            , a
 | 
			
		||||
                [ class (S.secondaryButton ++ "block font-semibold")
 | 
			
		||||
                , href "#"
 | 
			
		||||
                , onClick Disable
 | 
			
		||||
                ]
 | 
			
		||||
                [ text settings.cancelButton
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user