mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-05 22:55:58 +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:
parent
442b76c5af
commit
dd935454c9
@ -40,6 +40,7 @@ case class Duration(nanos: Long) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Duration {
|
object Duration {
|
||||||
|
val zero = Duration(0L)
|
||||||
|
|
||||||
def apply(d: SDur): Duration =
|
def apply(d: SDur): Duration =
|
||||||
Duration(d.toNanos)
|
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.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.common.{Banner, Pools, ThreadFactories}
|
import docspell.common._
|
||||||
|
|
||||||
import org.log4s._
|
import org.log4s._
|
||||||
|
|
||||||
@ -50,6 +50,10 @@ object Main extends IOApp {
|
|||||||
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
Some(cfg.fullTextSearch.solr.url).filter(_ => cfg.fullTextSearch.enabled)
|
||||||
)
|
)
|
||||||
logger.info(s"\n${banner.render("***>")}")
|
logger.info(s"\n${banner.render("***>")}")
|
||||||
|
if (EnvMode.current.isDev) {
|
||||||
|
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
||||||
|
}
|
||||||
|
|
||||||
val pools = for {
|
val pools = for {
|
||||||
cec <- connectEC
|
cec <- connectEC
|
||||||
bec <- blockingEC
|
bec <- blockingEC
|
||||||
|
@ -5,7 +5,7 @@ import java.nio.file.{Files, Paths}
|
|||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.common.{Banner, Pools, ThreadFactories}
|
import docspell.common._
|
||||||
|
|
||||||
import org.log4s._
|
import org.log4s._
|
||||||
|
|
||||||
@ -57,6 +57,10 @@ object Main extends IOApp {
|
|||||||
} yield Pools(cec, bec, blocker, rec)
|
} yield Pools(cec, bec, blocker, rec)
|
||||||
|
|
||||||
logger.info(s"\n${banner.render("***>")}")
|
logger.info(s"\n${banner.render("***>")}")
|
||||||
|
if (EnvMode.current.isDev) {
|
||||||
|
logger.warn(">>>>> Docspell is running in DEV mode! <<<<<")
|
||||||
|
}
|
||||||
|
|
||||||
pools.use(p =>
|
pools.use(p =>
|
||||||
RestServer
|
RestServer
|
||||||
.stream[IO](cfg, p)
|
.stream[IO](cfg, p)
|
||||||
|
@ -5,7 +5,8 @@ import cats.implicits._
|
|||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
|
|
||||||
import docspell.backend.auth.AuthToken
|
import docspell.backend.auth.AuthToken
|
||||||
import docspell.common.Pools
|
import docspell.common._
|
||||||
|
import docspell.restserver.http4s.EnvMiddleware
|
||||||
import docspell.restserver.routes._
|
import docspell.restserver.routes._
|
||||||
import docspell.restserver.webapp._
|
import docspell.restserver.webapp._
|
||||||
|
|
||||||
@ -39,9 +40,9 @@ object RestServer {
|
|||||||
adminRoutes(cfg, restApp)
|
adminRoutes(cfg, restApp)
|
||||||
},
|
},
|
||||||
"/api/doc" -> templates.doc,
|
"/api/doc" -> templates.doc,
|
||||||
"/app/assets" -> WebjarRoutes.appRoutes[F](pools.blocker),
|
"/app/assets" -> EnvMiddleware(WebjarRoutes.appRoutes[F](pools.blocker)),
|
||||||
"/app" -> templates.app,
|
"/app" -> EnvMiddleware(templates.app),
|
||||||
"/sw.js" -> templates.serviceWorker,
|
"/sw.js" -> EnvMiddleware(templates.serviceWorker),
|
||||||
"/" -> redirectTo("/app")
|
"/" -> redirectTo("/app")
|
||||||
).orNotFound
|
).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,
|
fullTextSearchEnabled: Boolean,
|
||||||
maxPageSize: Int,
|
maxPageSize: Int,
|
||||||
maxNoteLength: Int,
|
maxNoteLength: Int,
|
||||||
showClassificationSettings: Boolean
|
showClassificationSettings: Boolean,
|
||||||
|
uiVersion: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
object Flags {
|
object Flags {
|
||||||
def apply(cfg: Config): Flags =
|
def apply(cfg: Config, uiVersion: Int): Flags =
|
||||||
Flags(
|
Flags(
|
||||||
cfg.appName,
|
cfg.appName,
|
||||||
getBaseUrl(cfg),
|
getBaseUrl(cfg),
|
||||||
@ -32,7 +33,8 @@ object Flags {
|
|||||||
cfg.fullTextSearch.enabled,
|
cfg.fullTextSearch.enabled,
|
||||||
cfg.maxItemPageSize,
|
cfg.maxItemPageSize,
|
||||||
cfg.maxNoteLength,
|
cfg.maxNoteLength,
|
||||||
cfg.showClassificationSettings
|
cfg.showClassificationSettings,
|
||||||
|
uiVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
private def getBaseUrl(cfg: Config): String =
|
private def getBaseUrl(cfg: Config): String =
|
||||||
|
@ -14,6 +14,8 @@ import org.http4s.HttpRoutes
|
|||||||
import org.http4s._
|
import org.http4s._
|
||||||
import org.http4s.dsl.Http4sDsl
|
import org.http4s.dsl.Http4sDsl
|
||||||
import org.http4s.headers._
|
import org.http4s.headers._
|
||||||
|
import org.http4s.util.CaseInsensitiveString
|
||||||
|
import org.http4s.util.Writer
|
||||||
import org.log4s._
|
import org.log4s._
|
||||||
import yamusca.implicits._
|
import yamusca.implicits._
|
||||||
import yamusca.imports._
|
import yamusca.imports._
|
||||||
@ -24,6 +26,27 @@ object TemplateRoutes {
|
|||||||
val `text/html` = new MediaType("text", "html")
|
val `text/html` = new MediaType("text", "html")
|
||||||
val `application/javascript` = new MediaType("application", "javascript")
|
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[_]] {
|
trait InnerRoutes[F[_]] {
|
||||||
def doc: HttpRoutes[F]
|
def doc: HttpRoutes[F]
|
||||||
def app: HttpRoutes[F]
|
def app: HttpRoutes[F]
|
||||||
@ -53,22 +76,24 @@ object TemplateRoutes {
|
|||||||
} yield resp
|
} yield resp
|
||||||
}
|
}
|
||||||
def app =
|
def app =
|
||||||
HttpRoutes.of[F] { case GET -> _ =>
|
HttpRoutes.of[F] { case req @ GET -> _ =>
|
||||||
for {
|
for {
|
||||||
templ <- indexTemplate
|
templ <- indexTemplate
|
||||||
|
uiv = req.headers.get(UiVersion).map(_.version).getOrElse(1)
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
IndexData(cfg).render(templ),
|
IndexData(cfg, uiv).render(templ),
|
||||||
`Content-Type`(`text/html`, Charset.`UTF-8`)
|
`Content-Type`(`text/html`, Charset.`UTF-8`)
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
}
|
}
|
||||||
|
|
||||||
def serviceWorker =
|
def serviceWorker =
|
||||||
HttpRoutes.of[F] { case GET -> _ =>
|
HttpRoutes.of[F] { case req @ GET -> _ =>
|
||||||
for {
|
for {
|
||||||
templ <- swTemplate
|
templ <- swTemplate
|
||||||
|
uiv = req.headers.get(UiVersion).map(_.version).getOrElse(1)
|
||||||
resp <- Ok(
|
resp <- Ok(
|
||||||
IndexData(cfg).render(templ),
|
IndexData(cfg, uiv).render(templ),
|
||||||
`Content-Type`(`application/javascript`, Charset.`UTF-8`)
|
`Content-Type`(`application/javascript`, Charset.`UTF-8`)
|
||||||
)
|
)
|
||||||
} yield resp
|
} yield resp
|
||||||
@ -134,22 +159,28 @@ object TemplateRoutes {
|
|||||||
|
|
||||||
object IndexData {
|
object IndexData {
|
||||||
|
|
||||||
def apply(cfg: Config): IndexData =
|
def apply(cfg: Config, uiVersion: Int): IndexData =
|
||||||
IndexData(
|
IndexData(
|
||||||
Flags(cfg),
|
Flags(cfg, uiVersion),
|
||||||
Seq(
|
chooseUi(uiVersion),
|
||||||
"/app/assets" + Webjars.fomanticslimdefault + "/semantic.min.css",
|
|
||||||
s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.css"
|
|
||||||
),
|
|
||||||
Seq(
|
Seq(
|
||||||
"/app/assets" + Webjars.clipboardjs + "/clipboard.min.js",
|
"/app/assets" + Webjars.clipboardjs + "/clipboard.min.js",
|
||||||
s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell-app.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}/favicon",
|
||||||
s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell.js",
|
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] =
|
implicit def yamuscaValueConverter: ValueConverter[IndexData] =
|
||||||
ValueConverter.deriveConverter[IndexData]
|
ValueConverter.deriveConverter[IndexData]
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ import cats.data.Kleisli
|
|||||||
import cats.data.OptionT
|
import cats.data.OptionT
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
|
|
||||||
import org.http4s.HttpRoutes
|
import docspell.common._
|
||||||
import org.http4s.Method
|
|
||||||
import org.http4s.Response
|
import org.http4s._
|
||||||
import org.http4s.StaticFile
|
|
||||||
|
|
||||||
object WebjarRoutes {
|
object WebjarRoutes {
|
||||||
|
|
||||||
@ -37,12 +36,13 @@ object WebjarRoutes {
|
|||||||
if (p.contains("..") || !suffixes.exists(p.endsWith(_)))
|
if (p.contains("..") || !suffixes.exists(p.endsWith(_)))
|
||||||
OptionT.pure(Response.notFound[F])
|
OptionT.pure(Response.notFound[F])
|
||||||
else
|
else
|
||||||
StaticFile.fromResource(
|
StaticFile
|
||||||
s"/META-INF/resources/webjars$p",
|
.fromResource(
|
||||||
blocker,
|
s"/META-INF/resources/webjars$p",
|
||||||
Some(req),
|
blocker,
|
||||||
true
|
Some(req),
|
||||||
)
|
EnvMode.current.isProd
|
||||||
|
)
|
||||||
case _ =>
|
case _ =>
|
||||||
OptionT.none
|
OptionT.none
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,8 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body id="docspell-app">
|
||||||
<div id="docspell-app">
|
<!-- everything in here gets replaced by elm, including the body tag -->
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
var storedAccount = localStorage.getItem('account');
|
var storedAccount = localStorage.getItem('account');
|
||||||
|
@ -50,6 +50,7 @@ module Api exposing
|
|||||||
, getJobQueueState
|
, getJobQueueState
|
||||||
, getJobQueueStateIn
|
, getJobQueueStateIn
|
||||||
, getMailSettings
|
, getMailSettings
|
||||||
|
, getNewUi
|
||||||
, getNotifyDueItems
|
, getNotifyDueItems
|
||||||
, getOrgFull
|
, getOrgFull
|
||||||
, getOrgLight
|
, getOrgLight
|
||||||
@ -120,6 +121,7 @@ module Api exposing
|
|||||||
, startOnceScanMailbox
|
, startOnceScanMailbox
|
||||||
, startReIndex
|
, startReIndex
|
||||||
, submitNotifyDueItems
|
, submitNotifyDueItems
|
||||||
|
, toggleNewUi
|
||||||
, toggleTags
|
, toggleTags
|
||||||
, unconfirmMultiple
|
, unconfirmMultiple
|
||||||
, updateNotifyDueItems
|
, 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
|
--- Helper
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ module App.Data exposing
|
|||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.AuthResult exposing (AuthResult)
|
import Api.Model.AuthResult exposing (AuthResult)
|
||||||
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.VersionInfo exposing (VersionInfo)
|
import Api.Model.VersionInfo exposing (VersionInfo)
|
||||||
import Browser exposing (UrlRequest)
|
import Browser exposing (UrlRequest)
|
||||||
import Browser.Navigation exposing (Key)
|
import Browser.Navigation exposing (Key)
|
||||||
@ -45,6 +46,7 @@ type alias Model =
|
|||||||
, userMenuOpen : Bool
|
, userMenuOpen : Bool
|
||||||
, subs : Sub Msg
|
, subs : Sub Msg
|
||||||
, uiSettings : UiSettings
|
, uiSettings : UiSettings
|
||||||
|
, sidebarVisible : Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ init key url flags_ settings =
|
|||||||
, userMenuOpen = False
|
, userMenuOpen = False
|
||||||
, subs = Sub.none
|
, subs = Sub.none
|
||||||
, uiSettings = settings
|
, uiSettings = settings
|
||||||
|
, sidebarVisible = settings.sideMenuVisible
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Cmd.map UserSettingsMsg uc
|
[ Cmd.map UserSettingsMsg uc
|
||||||
@ -145,6 +148,8 @@ type Msg
|
|||||||
| ToggleNavMenu
|
| ToggleNavMenu
|
||||||
| ToggleUserMenu
|
| ToggleUserMenu
|
||||||
| GetUiSettings UiSettings
|
| GetUiSettings UiSettings
|
||||||
|
| ToggleSidebar
|
||||||
|
| ToggleDarkMode
|
||||||
|
|
||||||
|
|
||||||
defaultPage : Flags -> Page
|
defaultPage : Flags -> Page
|
||||||
|
@ -8,6 +8,7 @@ import App.Data exposing (..)
|
|||||||
import Browser exposing (UrlRequest(..))
|
import Browser exposing (UrlRequest(..))
|
||||||
import Browser.Navigation as Nav
|
import Browser.Navigation as Nav
|
||||||
import Data.Flags
|
import Data.Flags
|
||||||
|
import Data.UiTheme
|
||||||
import Page exposing (Page(..))
|
import Page exposing (Page(..))
|
||||||
import Page.CollectiveSettings.Data
|
import Page.CollectiveSettings.Data
|
||||||
import Page.CollectiveSettings.Update
|
import Page.CollectiveSettings.Update
|
||||||
@ -47,6 +48,38 @@ update msg model =
|
|||||||
updateWithSub : Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
|
updateWithSub : Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
|
||||||
updateWithSub msg model =
|
updateWithSub msg model =
|
||||||
case msg of
|
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 ->
|
HomeMsg lm ->
|
||||||
updateHome lm model
|
updateHome lm model
|
||||||
|
|
||||||
@ -213,8 +246,17 @@ updateWithSub msg model =
|
|||||||
)
|
)
|
||||||
|
|
||||||
GetUiSettings settings ->
|
GetUiSettings settings ->
|
||||||
|
let
|
||||||
|
setTheme =
|
||||||
|
Ports.setUiTheme settings.uiTheme
|
||||||
|
in
|
||||||
Util.Update.andThen2
|
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
|
, updateHome Page.Home.Data.UiSettingsUpdated
|
||||||
, updateItemDetail Page.ItemDetail.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
|
, getAddress
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Address exposing (Address)
|
import Api.Model.Address exposing (Address)
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.List
|
import Util.List
|
||||||
|
|
||||||
|
|
||||||
@ -150,3 +153,81 @@ view settings model =
|
|||||||
, Html.map CountryMsg (Comp.Dropdown.view settings model.country)
|
, 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.AttachmentMeta exposing (AttachmentMeta)
|
import Api.Model.AttachmentMeta exposing (AttachmentMeta)
|
||||||
import Api.Model.ItemProposals exposing (ItemProposals)
|
import Api.Model.ItemProposals exposing (ItemProposals)
|
||||||
import Api.Model.Label exposing (Label)
|
import Api.Model.Label exposing (Label)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Time
|
import Util.Time
|
||||||
|
|
||||||
@ -58,6 +61,10 @@ update msg model =
|
|||||||
{ model | meta = Failure (Util.Http.errorToString err) }
|
{ model | meta = Failure (Util.Http.errorToString err) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
div []
|
div []
|
||||||
@ -207,3 +214,156 @@ renderLabel label =
|
|||||||
, String.fromInt label.endPos |> text
|
, 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 Data.BasicSize exposing (BasicSize)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck)
|
import Html.Events exposing (onCheck)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
@ -17,6 +23,10 @@ update msg =
|
|||||||
Just bs
|
Just bs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : String -> BasicSize -> Html Msg
|
view : String -> BasicSize -> Html Msg
|
||||||
view labelTxt current =
|
view labelTxt current =
|
||||||
div [ class "grouped fields" ]
|
div [ class "grouped fields" ]
|
||||||
@ -38,3 +48,32 @@ makeField current element =
|
|||||||
, label [] [ text (Data.BasicSize.label 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
|
, initDefault
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -17,6 +18,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Time
|
import Util.Time
|
||||||
@ -124,6 +126,10 @@ update flags ev msg model =
|
|||||||
( m, Cmd.none, Unknown event )
|
( m, Cmd.none, Unknown event )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : String -> CalEvent -> Model -> Html Msg
|
view : String -> CalEvent -> Model -> Html Msg
|
||||||
view extraClasses ev model =
|
view extraClasses ev model =
|
||||||
let
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.PasswordChange exposing (PasswordChange)
|
import Api.Model.PasswordChange exposing (PasswordChange)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.PasswordInput
|
import Comp.PasswordInput
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +91,7 @@ validateModel model =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Update
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
||||||
@ -174,7 +177,7 @@ update flags msg model =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- View
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
@ -239,3 +242,95 @@ view model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -15,6 +16,7 @@ import Comp.Dropdown
|
|||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
import Data.CalEvent exposing (CalEvent)
|
import Data.CalEvent exposing (CalEvent)
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.ListType exposing (ListType)
|
import Data.ListType exposing (ListType)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -23,6 +25,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Http
|
import Http
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Styles as S
|
||||||
import Util.Tag
|
import Util.Tag
|
||||||
|
|
||||||
|
|
||||||
@ -177,6 +180,10 @@ update flags msg model =
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
let
|
let
|
||||||
@ -228,3 +235,66 @@ Use an empty whitelist to disable auto tagging.
|
|||||||
(Comp.CalEventInput.view "" (Data.Validated.value model.schedule) model.scheduleModel)
|
(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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
|
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.ClassifierSettingsForm
|
import Comp.ClassifierSettingsForm
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
|
import Comp.MenuBar as MB
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Language exposing (Language)
|
import Data.Language exposing (Language)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -20,6 +24,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -192,6 +197,10 @@ update flags msg model =
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Flags -> UiSettings -> Model -> Html Msg
|
view : Flags -> UiSettings -> Model -> Html Msg
|
||||||
view flags settings model =
|
view flags settings model =
|
||||||
div
|
div
|
||||||
@ -328,3 +337,165 @@ renderResultMessage result =
|
|||||||
|> Maybe.withDefault ""
|
|> Maybe.withDefault ""
|
||||||
|> text
|
|> 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
@ -13,6 +14,7 @@ import Dict exposing (Dict)
|
|||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -169,3 +171,92 @@ chooseColor tagger colors mtext =
|
|||||||
in
|
in
|
||||||
div [ class "ui labels" ] <|
|
div [ class "ui labels" ] <|
|
||||||
List.map renderLabel colors
|
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
|
, update
|
||||||
, view
|
, view
|
||||||
, view1
|
, view1
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Contact exposing (Contact)
|
import Api.Model.Contact exposing (Contact)
|
||||||
import Comp.Dropdown
|
import Comp.Basic as B
|
||||||
|
import Comp.FixedDropdown
|
||||||
import Data.ContactType exposing (ContactType)
|
import Data.ContactType exposing (ContactType)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ items : List Contact
|
{ items : List Contact
|
||||||
, kind : Comp.Dropdown.Model ContactType
|
, kind : Comp.FixedDropdown.Model ContactType
|
||||||
|
, selectedKind : Maybe ContactType
|
||||||
, value : String
|
, value : String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,17 +32,8 @@ emptyModel : Model
|
|||||||
emptyModel =
|
emptyModel =
|
||||||
{ items = []
|
{ items = []
|
||||||
, kind =
|
, kind =
|
||||||
Comp.Dropdown.makeSingleList
|
Comp.FixedDropdown.initMap Data.ContactType.toString Data.ContactType.all
|
||||||
{ makeOption =
|
, selectedKind = List.head Data.ContactType.all
|
||||||
\ct ->
|
|
||||||
{ value = Data.ContactType.toString ct
|
|
||||||
, text = Data.ContactType.toString ct
|
|
||||||
, additional = ""
|
|
||||||
}
|
|
||||||
, placeholder = ""
|
|
||||||
, options = Data.ContactType.all
|
|
||||||
, selected = List.head Data.ContactType.all
|
|
||||||
}
|
|
||||||
, value = ""
|
, value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,9 +43,16 @@ getContacts model =
|
|||||||
List.filter (\c -> c.value /= "") model.items
|
List.filter (\c -> c.value /= "") model.items
|
||||||
|
|
||||||
|
|
||||||
|
makeDropdownItem : ContactType -> Comp.FixedDropdown.Item ContactType
|
||||||
|
makeDropdownItem ct =
|
||||||
|
{ id = ct
|
||||||
|
, display = Data.ContactType.toString ct
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= SetValue String
|
= SetValue String
|
||||||
| TypeMsg (Comp.Dropdown.Msg ContactType)
|
| TypeMsg (Comp.FixedDropdown.Msg ContactType)
|
||||||
| AddContact
|
| AddContact
|
||||||
| Select Contact
|
| Select Contact
|
||||||
| SetItems (List Contact)
|
| SetItems (List Contact)
|
||||||
@ -67,25 +69,36 @@ update msg model =
|
|||||||
|
|
||||||
TypeMsg m ->
|
TypeMsg m ->
|
||||||
let
|
let
|
||||||
( m1, c1 ) =
|
( m1, sel ) =
|
||||||
Comp.Dropdown.update m model.kind
|
Comp.FixedDropdown.update m model.kind
|
||||||
|
|
||||||
|
newSel =
|
||||||
|
case sel of
|
||||||
|
Just _ ->
|
||||||
|
sel
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
model.selectedKind
|
||||||
in
|
in
|
||||||
( { model | kind = m1 }, Cmd.map TypeMsg c1 )
|
( { model | kind = m1, selectedKind = newSel }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
AddContact ->
|
AddContact ->
|
||||||
if model.value == "" then
|
if model.value == "" then
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
else
|
else
|
||||||
let
|
case model.selectedKind of
|
||||||
kind =
|
|
||||||
Comp.Dropdown.getSelected model.kind
|
|
||||||
|> List.head
|
|
||||||
|> Maybe.map Data.ContactType.toString
|
|
||||||
in
|
|
||||||
case kind of
|
|
||||||
Just k ->
|
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
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -96,13 +109,14 @@ update msg model =
|
|||||||
let
|
let
|
||||||
newItems =
|
newItems =
|
||||||
List.filter (\c -> c /= contact) model.items
|
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
|
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
|
view : UiSettings -> Model -> Html Msg
|
||||||
@ -111,7 +125,7 @@ view settings model =
|
|||||||
|
|
||||||
|
|
||||||
view1 : UiSettings -> Bool -> Model -> Html Msg
|
view1 : UiSettings -> Bool -> Model -> Html Msg
|
||||||
view1 settings compact model =
|
view1 _ compact model =
|
||||||
div []
|
div []
|
||||||
[ div [ classList [ ( "fields", not compact ) ] ]
|
[ div [ classList [ ( "fields", not compact ) ] ]
|
||||||
[ div
|
[ div
|
||||||
@ -120,7 +134,11 @@ view1 settings compact model =
|
|||||||
, ( "four wide", not compact )
|
, ( "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
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
@ -162,3 +180,73 @@ renderItem contact =
|
|||||||
]
|
]
|
||||||
, text contact.value
|
, 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
|
, makeField
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.CustomField exposing (CustomField)
|
import Api.Model.CustomField exposing (CustomField)
|
||||||
import Api.Model.NewCustomField exposing (NewCustomField)
|
import Api.Model.NewCustomField exposing (NewCustomField)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.CustomFieldType exposing (CustomFieldType)
|
import Data.CustomFieldType exposing (CustomFieldType)
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Validated exposing (Validated)
|
import Data.Validated exposing (Validated)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -304,3 +309,157 @@ viewButtons model =
|
|||||||
[ text "Delete"
|
[ 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
|
, update
|
||||||
, updateSearch
|
, updateSearch
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.CustomField exposing (CustomField)
|
import Api.Model.CustomField exposing (CustomField)
|
||||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
||||||
import Comp.DatePicker
|
import Comp.DatePicker
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Data.CustomFieldType exposing (CustomFieldType)
|
import Data.CustomFieldType exposing (CustomFieldType)
|
||||||
import Data.Icons as Icons
|
import Data.Icons as Icons
|
||||||
import Data.Money
|
import Data.Money
|
||||||
@ -21,6 +23,7 @@ import DatePicker exposing (DatePicker)
|
|||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -329,11 +332,6 @@ hasWildCards msg =
|
|||||||
--- View
|
--- View
|
||||||
|
|
||||||
|
|
||||||
mkLabel : Model -> String
|
|
||||||
mkLabel model =
|
|
||||||
Maybe.withDefault model.field.name model.field.label
|
|
||||||
|
|
||||||
|
|
||||||
removeButton : String -> Html Msg
|
removeButton : String -> Html Msg
|
||||||
removeButton classes =
|
removeButton classes =
|
||||||
a
|
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
|
--- Helper
|
||||||
|
|
||||||
|
|
||||||
|
mkLabel : Model -> String
|
||||||
|
mkLabel model =
|
||||||
|
Maybe.withDefault model.field.name model.field.label
|
||||||
|
|
||||||
|
|
||||||
string2Float : String -> Result String Float
|
string2Float : String -> Result String Float
|
||||||
string2Float str =
|
string2Float str =
|
||||||
case String.toFloat str of
|
case String.toFloat str of
|
||||||
|
@ -5,18 +5,23 @@ module Comp.CustomFieldManage exposing
|
|||||||
, init
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.CustomField exposing (CustomField)
|
import Api.Model.CustomField exposing (CustomField)
|
||||||
import Api.Model.CustomFieldList exposing (CustomFieldList)
|
import Api.Model.CustomFieldList exposing (CustomFieldList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.CustomFieldForm
|
import Comp.CustomFieldForm
|
||||||
import Comp.CustomFieldTable
|
import Comp.CustomFieldTable
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
|
import Util.CustomField
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -192,3 +197,69 @@ viewTable model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, update
|
||||||
, updateSearch
|
, updateSearch
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -22,12 +23,14 @@ import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
|||||||
import Comp.CustomFieldInput
|
import Comp.CustomFieldInput
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
import Data.CustomFieldChange exposing (CustomFieldChange(..))
|
import Data.CustomFieldChange exposing (CustomFieldChange(..))
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.CustomField
|
import Util.CustomField
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -148,18 +151,18 @@ mkItem f =
|
|||||||
Comp.FixedDropdown.Item f (Maybe.withDefault f.name f.label)
|
Comp.FixedDropdown.Item f (Maybe.withDefault f.name f.label)
|
||||||
|
|
||||||
|
|
||||||
update : Msg -> Model -> UpdateResult
|
update : Flags -> Msg -> Model -> UpdateResult
|
||||||
update =
|
update =
|
||||||
update1 False
|
update1 False
|
||||||
|
|
||||||
|
|
||||||
updateSearch : Msg -> Model -> UpdateResult
|
updateSearch : Flags -> Msg -> Model -> UpdateResult
|
||||||
updateSearch =
|
updateSearch =
|
||||||
update1 True
|
update1 True
|
||||||
|
|
||||||
|
|
||||||
update1 : Bool -> Msg -> Model -> UpdateResult
|
update1 : Bool -> Flags -> Msg -> Model -> UpdateResult
|
||||||
update1 forSearch msg model =
|
update1 forSearch flags msg model =
|
||||||
case msg of
|
case msg of
|
||||||
CreateNewField ->
|
CreateNewField ->
|
||||||
UpdateResult model Cmd.none FieldCreateNew
|
UpdateResult model Cmd.none FieldCreateNew
|
||||||
@ -195,7 +198,7 @@ update1 forSearch msg model =
|
|||||||
in
|
in
|
||||||
case sel of
|
case sel of
|
||||||
Just field ->
|
Just field ->
|
||||||
update (ApplyField field) model
|
update flags (ApplyField field) model
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
UpdateResult model_ Cmd.none NoFieldChange
|
UpdateResult model_ Cmd.none NoFieldChange
|
||||||
@ -213,11 +216,12 @@ update1 forSearch msg model =
|
|||||||
|
|
||||||
-- have to re-state the open menu when this is invoked
|
-- have to re-state the open menu when this is invoked
|
||||||
-- from a click in the dropdown
|
-- from a click in the dropdown
|
||||||
|
-- this hack is only required for the semantic-ui version
|
||||||
fSelectDropdown =
|
fSelectDropdown =
|
||||||
fSelect.dropdown
|
fSelect.dropdown
|
||||||
|
|
||||||
dropdownOpen =
|
dropdownOpen =
|
||||||
{ fSelectDropdown | menuOpen = True }
|
{ fSelectDropdown | menuOpen = flags.config.uiVersion /= 2 }
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -279,7 +283,7 @@ update1 forSearch msg model =
|
|||||||
NoFieldChange
|
NoFieldChange
|
||||||
in
|
in
|
||||||
if res.result == Comp.CustomFieldInput.RemoveField then
|
if res.result == Comp.CustomFieldInput.RemoveField then
|
||||||
update (RemoveField field) model_
|
update flags (RemoveField field) model_
|
||||||
|
|
||||||
else
|
else
|
||||||
UpdateResult model_ cmd_ result
|
UpdateResult model_ cmd_ result
|
||||||
@ -327,6 +331,10 @@ type alias ViewSettings =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : ViewSettings -> Model -> Html Msg
|
view : ViewSettings -> Model -> Html Msg
|
||||||
view viewSettings model =
|
view viewSettings model =
|
||||||
div [ class viewSettings.classes ]
|
div [ class viewSettings.classes ]
|
||||||
@ -387,3 +395,80 @@ addFieldLink classes _ =
|
|||||||
]
|
]
|
||||||
[ i [ class "plus link icon" ] []
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.CustomField exposing (CustomField)
|
import Api.Model.CustomField exposing (CustomField)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Time
|
import Util.Time
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +42,10 @@ update msg model =
|
|||||||
( model, EditAction item )
|
( model, EditAction item )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> List CustomField -> Html Msg
|
view : Model -> List CustomField -> Html Msg
|
||||||
view _ items =
|
view _ items =
|
||||||
div []
|
div []
|
||||||
@ -86,3 +93,47 @@ viewItem item =
|
|||||||
|> text
|
|> 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
|
, editEquip
|
||||||
, editOrg
|
, editOrg
|
||||||
, editPerson
|
, editPerson
|
||||||
|
, formHeading
|
||||||
, initConcPerson
|
, initConcPerson
|
||||||
, initCorrPerson
|
, initCorrPerson
|
||||||
, initCustomField
|
, initCustomField
|
||||||
@ -14,7 +15,9 @@ module Comp.DetailEdit exposing
|
|||||||
, initTagByName
|
, initTagByName
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
, viewModal
|
, viewModal
|
||||||
|
, viewModal2
|
||||||
)
|
)
|
||||||
|
|
||||||
{-| Module for allowing to edit metadata in the item-edit menu.
|
{-| 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.Person exposing (Person)
|
||||||
import Api.Model.ReferenceList exposing (ReferenceList)
|
import Api.Model.ReferenceList exposing (ReferenceList)
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.CustomFieldForm
|
import Comp.CustomFieldForm
|
||||||
import Comp.EquipmentForm
|
import Comp.EquipmentForm
|
||||||
import Comp.OrgForm
|
import Comp.OrgForm
|
||||||
@ -45,6 +49,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
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
|
, setMkOption
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
, viewSingle
|
, viewSingle
|
||||||
|
, viewSingle2
|
||||||
)
|
)
|
||||||
|
|
||||||
{-| This needs to be rewritten from scratch!
|
{-| This needs to be rewritten from scratch!
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Api.Model.IdName exposing (IdName)
|
import Api.Model.IdName exposing (IdName)
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -123,7 +126,12 @@ makeSingle opts =
|
|||||||
, searchable = \n -> n > 0
|
, searchable = \n -> n > 0
|
||||||
, makeOption = opts.makeOption
|
, makeOption = opts.makeOption
|
||||||
, labelColor = \_ -> \_ -> ""
|
, labelColor = \_ -> \_ -> ""
|
||||||
, placeholder = opts.placeholder
|
, placeholder =
|
||||||
|
if opts.placeholder == "" then
|
||||||
|
"Select…"
|
||||||
|
|
||||||
|
else
|
||||||
|
opts.placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -181,6 +189,7 @@ type Msg a
|
|||||||
| ToggleMenu
|
| ToggleMenu
|
||||||
| AddItem (Item a)
|
| AddItem (Item a)
|
||||||
| RemoveItem (Item a)
|
| RemoveItem (Item a)
|
||||||
|
| RemoveItem2 (Item a)
|
||||||
| Filter String
|
| Filter String
|
||||||
| ShowMenu Bool
|
| ShowMenu Bool
|
||||||
| KeyPress Int
|
| KeyPress Int
|
||||||
@ -333,6 +342,9 @@ isDropdownChangeMsg cm =
|
|||||||
RemoveItem _ ->
|
RemoveItem _ ->
|
||||||
True
|
True
|
||||||
|
|
||||||
|
RemoveItem2 _ ->
|
||||||
|
True
|
||||||
|
|
||||||
KeyPress code ->
|
KeyPress code ->
|
||||||
Util.Html.intToKeyCode code
|
Util.Html.intToKeyCode code
|
||||||
|> Maybe.map (\c -> c == Util.Html.Enter || c == Util.Html.ESC)
|
|> Maybe.map (\c -> c == Util.Html.Enter || c == Util.Html.ESC)
|
||||||
@ -379,6 +391,16 @@ update msg model =
|
|||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RemoveItem2 e ->
|
||||||
|
let
|
||||||
|
m =
|
||||||
|
deselectItem model e |> applyFilter ""
|
||||||
|
in
|
||||||
|
( -- Hack above only needed with semanticui
|
||||||
|
m
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
Filter str ->
|
Filter str ->
|
||||||
let
|
let
|
||||||
m =
|
m =
|
||||||
@ -609,3 +631,180 @@ renderOption item =
|
|||||||
[ text item.option.additional
|
[ 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
|
, setActive
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import Comp.Basic as B
|
||||||
import File exposing (File)
|
import File exposing (File)
|
||||||
import File.Select
|
import File.Select
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (..)
|
import Html.Events exposing (..)
|
||||||
|
import Styles as S
|
||||||
import Util.Html exposing (onDragEnter, onDragLeave, onDragOver, onDropFiles)
|
import Util.Html exposing (onDragEnter, onDragLeave, onDragOver, onDropFiles)
|
||||||
|
|
||||||
|
|
||||||
@ -28,27 +31,25 @@ type alias State =
|
|||||||
|
|
||||||
type alias Settings =
|
type alias Settings =
|
||||||
{ classList : State -> List ( String, Bool )
|
{ classList : State -> List ( String, Bool )
|
||||||
, contentTypes : List String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
defaultSettings : Settings
|
defaultSettings : Settings
|
||||||
defaultSettings =
|
defaultSettings =
|
||||||
{ classList = \_ -> [ ( "ui placeholder segment", True ) ]
|
{ classList = \_ -> [ ( "ui placeholder segment", True ) ]
|
||||||
, contentTypes = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ state : State
|
{ state : State
|
||||||
, settings : Settings
|
, contentTypes : List String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
init : Settings -> Model
|
init : List String -> Model
|
||||||
init settings =
|
init contentTypes =
|
||||||
{ state = State False True
|
{ state = State False True
|
||||||
, settings = settings
|
, contentTypes = contentTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ update msg model =
|
|||||||
( { model | state = ns }, Cmd.none, [] )
|
( { model | state = ns }, Cmd.none, [] )
|
||||||
|
|
||||||
PickFiles ->
|
PickFiles ->
|
||||||
( model, File.Select.files model.settings.contentTypes GotFiles, [] )
|
( model, File.Select.files model.contentTypes GotFiles, [] )
|
||||||
|
|
||||||
DragEnter ->
|
DragEnter ->
|
||||||
let
|
let
|
||||||
@ -99,7 +100,7 @@ update msg model =
|
|||||||
|
|
||||||
newFiles =
|
newFiles =
|
||||||
if model.state.active then
|
if model.state.active then
|
||||||
filterMime model.settings (file :: files)
|
filterMime model (file :: files)
|
||||||
|
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
@ -107,10 +108,10 @@ update msg model =
|
|||||||
( { model | state = ns }, Cmd.none, newFiles )
|
( { model | state = ns }, Cmd.none, newFiles )
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Settings -> Model -> Html Msg
|
||||||
view model =
|
view settings model =
|
||||||
div
|
div
|
||||||
[ classList (model.settings.classList model.state)
|
[ classList (settings.classList model.state)
|
||||||
, onDragEnter DragEnter
|
, onDragEnter DragEnter
|
||||||
, onDragOver DragEnter
|
, onDragOver DragEnter
|
||||||
, onDragLeave DragLeave
|
, onDragLeave DragLeave
|
||||||
@ -143,14 +144,57 @@ view model =
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
filterMime : Settings -> List File -> List File
|
filterMime : Model -> List File -> List File
|
||||||
filterMime settings files =
|
filterMime model files =
|
||||||
let
|
let
|
||||||
pred f =
|
pred f =
|
||||||
List.member (File.mime f) settings.contentTypes
|
List.member (File.mime f) model.contentTypes
|
||||||
in
|
in
|
||||||
if settings.contentTypes == [] then
|
if model.contentTypes == [] then
|
||||||
files
|
files
|
||||||
|
|
||||||
else
|
else
|
||||||
List.filter pred files
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.ContactList exposing (ContactList)
|
import Api.Model.ContactList exposing (ContactList)
|
||||||
|
import Comp.Dropdown
|
||||||
import Data.ContactType
|
import Data.ContactType
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Html exposing (onKeyUp)
|
import Util.Html exposing (onKeyUp)
|
||||||
import Util.List
|
import Util.List
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
@ -131,6 +135,10 @@ update flags current msg model =
|
|||||||
( model, Cmd.none, List.filter (\e -> e /= str) current )
|
( model, Cmd.none, List.filter (\e -> e /= str) current )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : List String -> Model -> Html Msg
|
view : List String -> Model -> Html Msg
|
||||||
view values model =
|
view values model =
|
||||||
div
|
div
|
||||||
@ -146,9 +154,9 @@ view values model =
|
|||||||
, placeholder "Recipients…"
|
, placeholder "Recipients…"
|
||||||
, onKeyUp KeyPress
|
, onKeyUp KeyPress
|
||||||
, onInput SetInput
|
, onInput SetInput
|
||||||
|
, value model.input
|
||||||
]
|
]
|
||||||
[ text model.input
|
[]
|
||||||
]
|
|
||||||
, renderMenu model
|
, renderMenu model
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -188,3 +196,71 @@ renderMenu model =
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
(List.map mkItem model.candidates)
|
(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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.PasswordInput
|
import Comp.PasswordInput
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.SSLType exposing (SSLType)
|
import Data.SSLType exposing (SSLType)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onInput)
|
import Html.Events exposing (onCheck, onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -176,6 +181,10 @@ update msg model =
|
|||||||
( { model | ignoreCertificates = not model.ignoreCertificates }, Cmd.none )
|
( { model | ignoreCertificates = not model.ignoreCertificates }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
div
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.EmailSettings
|
import Api.Model.EmailSettings
|
||||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.EmailSettingsForm
|
import Comp.EmailSettingsForm
|
||||||
import Comp.EmailSettingsTable
|
import Comp.EmailSettingsTable
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -20,6 +23,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -200,6 +204,10 @@ update flags msg model =
|
|||||||
( { model | loading = False }, Cmd.none )
|
( { model | loading = False }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
@ -287,3 +295,99 @@ viewForm settings model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.EmailSettings exposing (EmailSettings)
|
import Api.Model.EmailSettings exposing (EmailSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -42,6 +45,10 @@ update msg model =
|
|||||||
( { model | selected = Just ems }, Cmd.none )
|
( { model | selected = Just ems }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
table [ class "ui selectable pointer table" ]
|
table [ class "ui selectable pointer table" ]
|
||||||
@ -76,3 +83,45 @@ renderLine model ems =
|
|||||||
, td [] [ text hostport ]
|
, td [] [ text hostport ]
|
||||||
, td [] [ text ems.from ]
|
, 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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Equipment exposing (Equipment)
|
import Api.Model.Equipment exposing (Equipment)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.Equipment
|
import Api.Model.Equipment
|
||||||
import Api.Model.EquipmentList exposing (EquipmentList)
|
import Api.Model.EquipmentList exposing (EquipmentList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.EquipmentForm
|
import Comp.EquipmentForm
|
||||||
import Comp.EquipmentTable
|
import Comp.EquipmentTable
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -300,3 +304,124 @@ viewForm model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Equipment exposing (Equipment)
|
import Api.Model.Equipment exposing (Equipment)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -78,3 +81,34 @@ renderEquipmentLine model equip =
|
|||||||
[ text equip.name
|
[ 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 Data.Fields exposing (Field)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -67,3 +74,31 @@ fieldCheckbox selected field =
|
|||||||
, label [] [ text (Data.Fields.label 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
|
, initTuple
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
, viewString
|
, viewString
|
||||||
, viewStyled
|
, viewStyled
|
||||||
|
, viewStyled2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Html exposing (KeyCode(..), onKeyUpCode)
|
import Util.Html exposing (KeyCode(..), onKeyUpCode)
|
||||||
import Util.List
|
import Util.List
|
||||||
|
|
||||||
@ -34,6 +38,7 @@ type alias Model a =
|
|||||||
|
|
||||||
type Msg a
|
type Msg a
|
||||||
= SelectItem (Item a)
|
= SelectItem (Item a)
|
||||||
|
| SelectItem2 (Item a)
|
||||||
| ToggleMenu
|
| ToggleMenu
|
||||||
| KeyPress (Maybe KeyCode)
|
| KeyPress (Maybe KeyCode)
|
||||||
|
|
||||||
@ -122,24 +127,23 @@ update msg model =
|
|||||||
SelectItem item ->
|
SelectItem item ->
|
||||||
( model, Just item.id )
|
( model, Just item.id )
|
||||||
|
|
||||||
|
SelectItem2 item ->
|
||||||
|
( { model | menuOpen = False }, Just item.id )
|
||||||
|
|
||||||
KeyPress (Just Space) ->
|
KeyPress (Just Space) ->
|
||||||
update ToggleMenu model
|
update ToggleMenu model
|
||||||
|
|
||||||
KeyPress (Just Enter) ->
|
KeyPress (Just Enter) ->
|
||||||
if not model.menuOpen then
|
let
|
||||||
update ToggleMenu model
|
selected =
|
||||||
|
Util.List.find (isSelected model) model.options
|
||||||
|
in
|
||||||
|
case selected of
|
||||||
|
Just i ->
|
||||||
|
( { model | menuOpen = False }, Just i.id )
|
||||||
|
|
||||||
else
|
Nothing ->
|
||||||
let
|
( model, Nothing )
|
||||||
selected =
|
|
||||||
Util.List.find (isSelected model) model.options
|
|
||||||
in
|
|
||||||
case selected of
|
|
||||||
Just i ->
|
|
||||||
( { model | menuOpen = False }, Just i.id )
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
( model, Nothing )
|
|
||||||
|
|
||||||
KeyPress (Just Up) ->
|
KeyPress (Just Up) ->
|
||||||
movePrevious model
|
movePrevious model
|
||||||
@ -223,3 +227,63 @@ renderItems model item =
|
|||||||
]
|
]
|
||||||
[ text item.display
|
[ 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
|
, initEmpty
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -14,14 +15,16 @@ import Api.Model.IdName exposing (IdName)
|
|||||||
import Api.Model.IdResult exposing (IdResult)
|
import Api.Model.IdResult exposing (IdResult)
|
||||||
import Api.Model.NewFolder exposing (NewFolder)
|
import Api.Model.NewFolder exposing (NewFolder)
|
||||||
import Api.Model.User exposing (User)
|
import Api.Model.User exposing (User)
|
||||||
import Api.Model.UserList exposing (UserList)
|
import Comp.Basic as B
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -416,3 +419,178 @@ viewMember member =
|
|||||||
[ text member.name
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -15,11 +16,13 @@ import Api.Model.User exposing (User)
|
|||||||
import Api.Model.UserList exposing (UserList)
|
import Api.Model.UserList exposing (UserList)
|
||||||
import Comp.FolderDetail
|
import Comp.FolderDetail
|
||||||
import Comp.FolderTable
|
import Comp.FolderTable
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -235,3 +238,77 @@ viewTable model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, updateDrop
|
||||||
, view
|
, view
|
||||||
, viewDrop
|
, viewDrop
|
||||||
|
, viewDrop2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.FolderStats exposing (FolderStats)
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.FolderItem exposing (FolderItem)
|
import Api.Model.FolderItem exposing (FolderItem)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Html
|
import Util.Html
|
||||||
import Util.Time
|
import Util.Time
|
||||||
|
|
||||||
@ -40,6 +43,10 @@ update msg model =
|
|||||||
( model, EditAction item )
|
( model, EditAction item )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> List FolderItem -> Html Msg
|
view : Model -> List FolderItem -> Html Msg
|
||||||
view _ items =
|
view _ items =
|
||||||
div []
|
div []
|
||||||
@ -91,3 +98,61 @@ viewItem item =
|
|||||||
|> text
|
|> 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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.ImapSettings exposing (ImapSettings)
|
import Api.Model.ImapSettings exposing (ImapSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.PasswordInput
|
import Comp.PasswordInput
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.SSLType exposing (SSLType)
|
import Data.SSLType exposing (SSLType)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onInput)
|
import Html.Events exposing (onCheck, onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -168,6 +173,10 @@ update msg model =
|
|||||||
( { model | useOAuthToken = not model.useOAuthToken }, Cmd.none )
|
( { model | useOAuthToken = not model.useOAuthToken }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
div
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.ImapSettings
|
import Api.Model.ImapSettings
|
||||||
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.ImapSettingsForm
|
import Comp.ImapSettingsForm
|
||||||
import Comp.ImapSettingsTable
|
import Comp.ImapSettingsTable
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -20,6 +23,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -200,6 +204,10 @@ update flags msg model =
|
|||||||
( { model | loading = False }, Cmd.none )
|
( { model | loading = False }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
@ -287,3 +295,105 @@ viewForm settings model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.ImapSettings exposing (ImapSettings)
|
import Api.Model.ImapSettings exposing (ImapSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -42,6 +45,10 @@ update msg model =
|
|||||||
( { model | selected = Just ems }, Cmd.none )
|
( { model | selected = Just ems }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
table [ class "ui selectable pointer table" ]
|
table [ class "ui selectable pointer table" ]
|
||||||
@ -74,3 +81,41 @@ renderLine model ems =
|
|||||||
[ td [ class "collapsible" ] [ text ems.name ]
|
[ td [ class "collapsible" ] [ text ems.name ]
|
||||||
, td [] [ text hostport ]
|
, 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
|
, update
|
||||||
, view
|
, view
|
||||||
, viewWithInfo
|
, viewWithInfo
|
||||||
|
, viewWithInfo2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -133,3 +135,46 @@ viewWithInfo info nval classes model =
|
|||||||
[ Maybe.withDefault "" model.error |> text
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -25,6 +26,7 @@ import Html.Events exposing (onClick)
|
|||||||
import Markdown
|
import Markdown
|
||||||
import Page exposing (Page(..))
|
import Page exposing (Page(..))
|
||||||
import Set exposing (Set)
|
import Set exposing (Set)
|
||||||
|
import Styles as S
|
||||||
import Util.CustomField
|
import Util.CustomField
|
||||||
import Util.ItemDragDrop as DD
|
import Util.ItemDragDrop as DD
|
||||||
import Util.List
|
import Util.List
|
||||||
@ -91,6 +93,10 @@ currentPosition model item =
|
|||||||
1
|
1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Update
|
||||||
|
|
||||||
|
|
||||||
update : DD.Model -> Msg -> Model -> UpdateResult
|
update : DD.Model -> Msg -> Model -> UpdateResult
|
||||||
update ddm msg model =
|
update ddm msg model =
|
||||||
case msg of
|
case msg of
|
||||||
@ -129,6 +135,10 @@ update ddm msg model =
|
|||||||
UpdateResult model ddm Data.ItemSelection.Inactive target
|
UpdateResult model ddm Data.ItemSelection.Inactive target
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : ViewConfig -> UiSettings -> Model -> ItemLight -> Html Msg
|
view : ViewConfig -> UiSettings -> Model -> ItemLight -> Html Msg
|
||||||
view cfg settings model item =
|
view cfg settings model item =
|
||||||
let
|
let
|
||||||
@ -236,7 +246,7 @@ metaDataContent settings item =
|
|||||||
, title "Correspondent"
|
, title "Correspondent"
|
||||||
]
|
]
|
||||||
(Icons.correspondentIcon ""
|
(Icons.correspondentIcon ""
|
||||||
:: Comp.LinkTarget.makeCorrLink item SetLinkTarget
|
:: Comp.LinkTarget.makeCorrLink item [] SetLinkTarget
|
||||||
)
|
)
|
||||||
, div
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
@ -249,7 +259,7 @@ metaDataContent settings item =
|
|||||||
, title "Concerning"
|
, title "Concerning"
|
||||||
]
|
]
|
||||||
(Icons.concernedIcon
|
(Icons.concernedIcon
|
||||||
:: Comp.LinkTarget.makeConcLink item SetLinkTarget
|
:: Comp.LinkTarget.makeConcLink item [] SetLinkTarget
|
||||||
)
|
)
|
||||||
, div
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
@ -259,7 +269,7 @@ metaDataContent settings item =
|
|||||||
, title "Folder"
|
, title "Folder"
|
||||||
]
|
]
|
||||||
[ Icons.folderIcon ""
|
[ Icons.folderIcon ""
|
||||||
, Comp.LinkTarget.makeFolderLink item SetLinkTarget
|
, Comp.LinkTarget.makeFolderLink item [] SetLinkTarget
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, div [ class "right floated meta" ]
|
, 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 : ViewConfig -> String -> Bool
|
||||||
isSelected cfg id =
|
isSelected cfg id =
|
||||||
case cfg.selection of
|
case cfg.selection of
|
||||||
@ -553,3 +992,13 @@ isSelected cfg id =
|
|||||||
|
|
||||||
Data.ItemSelection.Inactive ->
|
Data.ItemSelection.Inactive ->
|
||||||
False
|
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
|
, update
|
||||||
, updateDrag
|
, updateDrag
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.ItemLight exposing (ItemLight)
|
import Api.Model.ItemLight exposing (ItemLight)
|
||||||
@ -23,6 +24,7 @@ import Dict exposing (Dict)
|
|||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Page exposing (Page(..))
|
import Page exposing (Page(..))
|
||||||
|
import Styles as S
|
||||||
import Util.ItemDragDrop as DD
|
import Util.ItemDragDrop as DD
|
||||||
import Util.List
|
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
|
--- Helpers
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,12 +3,14 @@ module Comp.ItemDetail exposing
|
|||||||
, emptyModel
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Browser.Navigation as Nav
|
import Browser.Navigation as Nav
|
||||||
import Comp.ItemDetail.Model exposing (Msg(..), UpdateResult)
|
import Comp.ItemDetail.Model exposing (Msg(..), UpdateResult)
|
||||||
import Comp.ItemDetail.Update
|
import Comp.ItemDetail.Update
|
||||||
import Comp.ItemDetail.View exposing (..)
|
import Comp.ItemDetail.View
|
||||||
|
import Comp.ItemDetail.View2
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.ItemNav exposing (ItemNav)
|
import Data.ItemNav exposing (ItemNav)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -33,3 +35,8 @@ update =
|
|||||||
view : ItemNav -> UiSettings -> Model -> Html Msg
|
view : ItemNav -> UiSettings -> Model -> Html Msg
|
||||||
view =
|
view =
|
||||||
Comp.ItemDetail.View.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
|
, customFieldThrottle : Throttle Msg
|
||||||
, allTags : List Tag
|
, allTags : List Tag
|
||||||
, allPersons : Dict String Person
|
, allPersons : Dict String Person
|
||||||
|
, attachmentDropdownOpen : Bool
|
||||||
|
, editMenuTabsOpen : Set String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ emptyModel =
|
|||||||
, attachMenuOpen = False
|
, attachMenuOpen = False
|
||||||
, menuOpen = False
|
, menuOpen = False
|
||||||
, tagModel =
|
, tagModel =
|
||||||
Util.Tag.makeDropdownModel
|
Util.Tag.makeDropdownModel2
|
||||||
, directionModel =
|
, directionModel =
|
||||||
Comp.Dropdown.makeSingleList
|
Comp.Dropdown.makeSingleList
|
||||||
{ makeOption =
|
{ makeOption =
|
||||||
@ -195,7 +197,7 @@ emptyModel =
|
|||||||
, pdfNativeView = Nothing
|
, pdfNativeView = Nothing
|
||||||
, deleteAttachConfirm = Comp.YesNoDimmer.emptyModel
|
, deleteAttachConfirm = Comp.YesNoDimmer.emptyModel
|
||||||
, addFilesOpen = False
|
, addFilesOpen = False
|
||||||
, addFilesModel = Comp.Dropzone.init Comp.Dropzone.defaultSettings
|
, addFilesModel = Comp.Dropzone.init []
|
||||||
, selectedFiles = []
|
, selectedFiles = []
|
||||||
, completed = Set.empty
|
, completed = Set.empty
|
||||||
, errored = Set.empty
|
, errored = Set.empty
|
||||||
@ -209,6 +211,8 @@ emptyModel =
|
|||||||
, customFieldThrottle = Throttle.create 1
|
, customFieldThrottle = Throttle.create 1
|
||||||
, allTags = []
|
, allTags = []
|
||||||
, allPersons = Dict.empty
|
, allPersons = Dict.empty
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
|
, editMenuTabsOpen = Set.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -297,6 +301,9 @@ type Msg
|
|||||||
| CustomFieldMsg Comp.CustomFieldMultiInput.Msg
|
| CustomFieldMsg Comp.CustomFieldMultiInput.Msg
|
||||||
| CustomFieldSaveResp CustomField String (Result Http.Error BasicResult)
|
| CustomFieldSaveResp CustomField String (Result Http.Error BasicResult)
|
||||||
| CustomFieldRemoveResp String (Result Http.Error BasicResult)
|
| CustomFieldRemoveResp String (Result Http.Error BasicResult)
|
||||||
|
| ToggleAttachmentDropdown
|
||||||
|
| ToggleAkkordionTab String
|
||||||
|
| ToggleOpenAllAkkordionTabs
|
||||||
|
|
||||||
|
|
||||||
type SaveNameState
|
type SaveNameState
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
module Comp.ItemDetail.EditMenu exposing
|
module Comp.ItemDetail.MultiEditMenu exposing
|
||||||
( Model
|
( Model
|
||||||
, Msg
|
, Msg
|
||||||
, SaveNameState(..)
|
, SaveNameState(..)
|
||||||
@ -7,6 +7,7 @@ module Comp.ItemDetail.EditMenu exposing
|
|||||||
, loadModel
|
, loadModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -14,7 +15,6 @@ import Api.Model.EquipmentList exposing (EquipmentList)
|
|||||||
import Api.Model.FolderItem exposing (FolderItem)
|
import Api.Model.FolderItem exposing (FolderItem)
|
||||||
import Api.Model.FolderList exposing (FolderList)
|
import Api.Model.FolderList exposing (FolderList)
|
||||||
import Api.Model.IdName exposing (IdName)
|
import Api.Model.IdName exposing (IdName)
|
||||||
import Api.Model.ItemProposals exposing (ItemProposals)
|
|
||||||
import Api.Model.PersonList exposing (PersonList)
|
import Api.Model.PersonList exposing (PersonList)
|
||||||
import Api.Model.ReferenceList exposing (ReferenceList)
|
import Api.Model.ReferenceList exposing (ReferenceList)
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
@ -23,9 +23,12 @@ import Comp.CustomFieldMultiInput
|
|||||||
import Comp.DatePicker
|
import Comp.DatePicker
|
||||||
import Comp.DetailEdit
|
import Comp.DetailEdit
|
||||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
||||||
|
import Comp.ItemDetail.FieldTabState as FTabState
|
||||||
import Comp.ItemDetail.FormChange exposing (FormChange(..))
|
import Comp.ItemDetail.FormChange exposing (FormChange(..))
|
||||||
|
import Comp.Tabs as TB
|
||||||
import Data.CustomFieldChange exposing (CustomFieldChange(..))
|
import Data.CustomFieldChange exposing (CustomFieldChange(..))
|
||||||
import Data.Direction exposing (Direction)
|
import Data.Direction exposing (Direction)
|
||||||
|
import Data.DropdownStyle
|
||||||
import Data.Fields
|
import Data.Fields
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Icons as Icons
|
import Data.Icons as Icons
|
||||||
@ -37,6 +40,8 @@ import Html.Events exposing (onClick, onInput)
|
|||||||
import Http
|
import Http
|
||||||
import Markdown
|
import Markdown
|
||||||
import Page exposing (Page(..))
|
import Page exposing (Page(..))
|
||||||
|
import Set exposing (Set)
|
||||||
|
import Styles as S
|
||||||
import Task
|
import Task
|
||||||
import Throttle exposing (Throttle)
|
import Throttle exposing (Throttle)
|
||||||
import Time
|
import Time
|
||||||
@ -71,7 +76,6 @@ type alias Model =
|
|||||||
, directionModel : Comp.Dropdown.Model Direction
|
, directionModel : Comp.Dropdown.Model Direction
|
||||||
, itemDatePicker : DatePicker
|
, itemDatePicker : DatePicker
|
||||||
, itemDate : Maybe Int
|
, itemDate : Maybe Int
|
||||||
, itemProposals : ItemProposals
|
|
||||||
, dueDate : Maybe Int
|
, dueDate : Maybe Int
|
||||||
, dueDatePicker : DatePicker
|
, dueDatePicker : DatePicker
|
||||||
, corrOrgModel : Comp.Dropdown.Model IdName
|
, corrOrgModel : Comp.Dropdown.Model IdName
|
||||||
@ -81,6 +85,7 @@ type alias Model =
|
|||||||
, modalEdit : Maybe Comp.DetailEdit.Model
|
, modalEdit : Maybe Comp.DetailEdit.Model
|
||||||
, tagEditMode : TagEditMode
|
, tagEditMode : TagEditMode
|
||||||
, customFieldModel : Comp.CustomFieldMultiInput.Model
|
, customFieldModel : Comp.CustomFieldMultiInput.Model
|
||||||
|
, openTabs : Set String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -107,12 +112,13 @@ type Msg
|
|||||||
| GetEquipResp (Result Http.Error EquipmentList)
|
| GetEquipResp (Result Http.Error EquipmentList)
|
||||||
| GetFolderResp (Result Http.Error FolderList)
|
| GetFolderResp (Result Http.Error FolderList)
|
||||||
| CustomFieldMsg Comp.CustomFieldMultiInput.Msg
|
| CustomFieldMsg Comp.CustomFieldMultiInput.Msg
|
||||||
|
| ToggleAkkordionTab String
|
||||||
|
|
||||||
|
|
||||||
init : Model
|
init : Model
|
||||||
init =
|
init =
|
||||||
{ tagModel =
|
{ tagModel =
|
||||||
Util.Tag.makeDropdownModel
|
Util.Tag.makeDropdownModel2
|
||||||
, directionModel =
|
, directionModel =
|
||||||
Comp.Dropdown.makeSingleList
|
Comp.Dropdown.makeSingleList
|
||||||
{ makeOption =
|
{ makeOption =
|
||||||
@ -155,12 +161,12 @@ init =
|
|||||||
, nameSaveThrottle = Throttle.create 1
|
, nameSaveThrottle = Throttle.create 1
|
||||||
, itemDatePicker = Comp.DatePicker.emptyModel
|
, itemDatePicker = Comp.DatePicker.emptyModel
|
||||||
, itemDate = Nothing
|
, itemDate = Nothing
|
||||||
, itemProposals = Api.Model.ItemProposals.empty
|
|
||||||
, dueDate = Nothing
|
, dueDate = Nothing
|
||||||
, dueDatePicker = Comp.DatePicker.emptyModel
|
, dueDatePicker = Comp.DatePicker.emptyModel
|
||||||
, modalEdit = Nothing
|
, modalEdit = Nothing
|
||||||
, tagEditMode = AddTags
|
, tagEditMode = AddTags
|
||||||
, customFieldModel = Comp.CustomFieldMultiInput.initWith []
|
, customFieldModel = Comp.CustomFieldMultiInput.initWith []
|
||||||
|
, openTabs = Set.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -563,7 +569,7 @@ update flags msg model =
|
|||||||
CustomFieldMsg lm ->
|
CustomFieldMsg lm ->
|
||||||
let
|
let
|
||||||
res =
|
res =
|
||||||
Comp.CustomFieldMultiInput.update lm model.customFieldModel
|
Comp.CustomFieldMultiInput.update flags lm model.customFieldModel
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model | customFieldModel = res.model }
|
{ model | customFieldModel = res.model }
|
||||||
@ -587,6 +593,17 @@ update flags msg model =
|
|||||||
in
|
in
|
||||||
UpdateResult model_ cmd_ Sub.none change
|
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 -> Sub Msg
|
||||||
nameThrottleSub model =
|
nameThrottleSub model =
|
||||||
@ -857,3 +874,286 @@ actionInputDatePicker =
|
|||||||
Comp.DatePicker.defaultSettings
|
Comp.DatePicker.defaultSettings
|
||||||
in
|
in
|
||||||
{ ds | containerClassList = [ ( "ui action input", True ) ] }
|
{ 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.Dropdown exposing (isDropdownChangeMsg)
|
||||||
import Comp.Dropzone
|
import Comp.Dropzone
|
||||||
import Comp.EquipmentForm
|
import Comp.EquipmentForm
|
||||||
|
import Comp.ItemDetail.EditForm
|
||||||
import Comp.ItemDetail.Model
|
import Comp.ItemDetail.Model
|
||||||
exposing
|
exposing
|
||||||
( AttachmentRename
|
( AttachmentRename
|
||||||
@ -863,7 +864,10 @@ update key flags inav settings msg model =
|
|||||||
case Dict.get id model.attachMeta of
|
case Dict.get id model.attachMeta of
|
||||||
Just _ ->
|
Just _ ->
|
||||||
resultModel
|
resultModel
|
||||||
{ model | attachMetaOpen = not model.attachMetaOpen }
|
{ model
|
||||||
|
| attachMetaOpen = not model.attachMetaOpen
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
|
}
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
let
|
let
|
||||||
@ -874,7 +878,11 @@ update key flags inav settings msg model =
|
|||||||
Dict.insert id am model.attachMeta
|
Dict.insert id am model.attachMeta
|
||||||
in
|
in
|
||||||
resultModelCmd
|
resultModelCmd
|
||||||
( { model | attachMeta = nextMeta, attachMetaOpen = True }
|
( { model
|
||||||
|
| attachMeta = nextMeta
|
||||||
|
, attachMetaOpen = True
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
|
}
|
||||||
, Cmd.map (AttachMetaMsg id) ac
|
, Cmd.map (AttachMetaMsg id) ac
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -901,6 +909,7 @@ update key flags inav settings msg model =
|
|||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
Just (not default)
|
Just (not default)
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteAttachConfirm attachId lmsg ->
|
DeleteAttachConfirm attachId lmsg ->
|
||||||
@ -933,7 +942,7 @@ update key flags inav settings msg model =
|
|||||||
inav
|
inav
|
||||||
settings
|
settings
|
||||||
(DeleteAttachConfirm id Comp.YesNoDimmer.activate)
|
(DeleteAttachConfirm id Comp.YesNoDimmer.activate)
|
||||||
model
|
{ model | attachmentDropdownOpen = False }
|
||||||
|
|
||||||
AddFilesToggle ->
|
AddFilesToggle ->
|
||||||
resultModel
|
resultModel
|
||||||
@ -964,7 +973,7 @@ update key flags inav settings msg model =
|
|||||||
resultModel
|
resultModel
|
||||||
{ model
|
{ model
|
||||||
| selectedFiles = []
|
| selectedFiles = []
|
||||||
, addFilesModel = Comp.Dropzone.init Comp.Dropzone.defaultSettings
|
, addFilesModel = Comp.Dropzone.init []
|
||||||
, completed = Set.empty
|
, completed = Set.empty
|
||||||
, errored = Set.empty
|
, errored = Set.empty
|
||||||
, loading = Dict.empty
|
, loading = Dict.empty
|
||||||
@ -1220,13 +1229,21 @@ update key flags inav settings msg model =
|
|||||||
in
|
in
|
||||||
case name of
|
case name of
|
||||||
Just n ->
|
Just n ->
|
||||||
resultModel { model | attachRename = Just (AttachmentRename id n) }
|
resultModel
|
||||||
|
{ model
|
||||||
|
| attachRename = Just (AttachmentRename id n)
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
|
}
|
||||||
|
|
||||||
Nothing ->
|
Nothing ->
|
||||||
resultModel model
|
resultModel model
|
||||||
|
|
||||||
Just _ ->
|
Just _ ->
|
||||||
resultModel { model | attachRename = Nothing }
|
resultModel
|
||||||
|
{ model
|
||||||
|
| attachRename = Nothing
|
||||||
|
, attachmentDropdownOpen = False
|
||||||
|
}
|
||||||
|
|
||||||
EditAttachNameCancel ->
|
EditAttachNameCancel ->
|
||||||
resultModel { model | attachRename = Nothing }
|
resultModel { model | attachRename = Nothing }
|
||||||
@ -1370,7 +1387,7 @@ update key flags inav settings msg model =
|
|||||||
CustomFieldMsg lm ->
|
CustomFieldMsg lm ->
|
||||||
let
|
let
|
||||||
result =
|
result =
|
||||||
Comp.CustomFieldMultiInput.update lm model.customFieldsModel
|
Comp.CustomFieldMultiInput.update flags lm model.customFieldsModel
|
||||||
|
|
||||||
cmd_ =
|
cmd_ =
|
||||||
Cmd.map CustomFieldMsg result.cmd
|
Cmd.map CustomFieldMsg result.cmd
|
||||||
@ -1460,6 +1477,36 @@ update key flags inav settings msg model =
|
|||||||
CustomFieldRemoveResp fieldId (Err _) ->
|
CustomFieldRemoveResp fieldId (Err _) ->
|
||||||
resultModel { model | customFieldSavingIcon = Dict.remove fieldId model.customFieldSavingIcon }
|
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
|
--- Helper
|
||||||
|
@ -525,7 +525,7 @@ renderItemInfo settings model =
|
|||||||
, title "Correspondent"
|
, title "Correspondent"
|
||||||
]
|
]
|
||||||
(Icons.correspondentIcon ""
|
(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.CorrOrg
|
||||||
|| Data.UiSettings.fieldVisible settings Data.Fields.CorrPerson
|
|| Data.UiSettings.fieldVisible settings Data.Fields.CorrPerson
|
||||||
@ -537,7 +537,7 @@ renderItemInfo settings model =
|
|||||||
, title "Concerning"
|
, title "Concerning"
|
||||||
]
|
]
|
||||||
(Icons.concernedIcon
|
(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.ConcEquip
|
||||||
|| Data.UiSettings.fieldVisible settings Data.Fields.ConcPerson
|
|| Data.UiSettings.fieldVisible settings Data.Fields.ConcPerson
|
||||||
@ -549,7 +549,7 @@ renderItemInfo settings model =
|
|||||||
, title "Folder"
|
, title "Folder"
|
||||||
]
|
]
|
||||||
[ Icons.folderIcon ""
|
[ Icons.folderIcon ""
|
||||||
, Comp.LinkTarget.makeFolderLink model.item SetLinkTarget
|
, Comp.LinkTarget.makeFolderLink model.item [] SetLinkTarget
|
||||||
]
|
]
|
||||||
, Data.UiSettings.fieldVisible settings Data.Fields.Folder
|
, Data.UiSettings.fieldVisible settings Data.Fields.Folder
|
||||||
)
|
)
|
||||||
@ -1093,7 +1093,11 @@ renderAddFilesForm model =
|
|||||||
[ h4 [ class "ui header" ]
|
[ h4 [ class "ui header" ]
|
||||||
[ text "Add more files to this item"
|
[ 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
|
, button
|
||||||
[ class "ui primary button"
|
[ class "ui primary button"
|
||||||
, href "#"
|
, 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
||||||
import Api.Model.SimpleMail exposing (SimpleMail)
|
import Api.Model.SimpleMail exposing (SimpleMail)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.EmailInput
|
import Comp.EmailInput
|
||||||
|
import Comp.MenuBar as MB
|
||||||
|
import Data.DropdownStyle
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -216,6 +221,10 @@ isValid model =
|
|||||||
== Nothing
|
== Nothing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
div
|
div
|
||||||
@ -293,3 +302,104 @@ view settings model =
|
|||||||
[ text "Cancel"
|
[ 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
|
, makeConcLink
|
||||||
, makeCorrLink
|
, makeCorrLink
|
||||||
, makeCustomFieldLink
|
, makeCustomFieldLink
|
||||||
|
, makeCustomFieldLink2
|
||||||
, makeFolderLink
|
, makeFolderLink
|
||||||
, makeSourceLink
|
, makeSourceLink
|
||||||
|
, makeTagIconLink
|
||||||
, makeTagLink
|
, makeTagLink
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.IdName exposing (IdName)
|
import Api.Model.IdName exposing (IdName)
|
||||||
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
import Api.Model.ItemFieldValue exposing (ItemFieldValue)
|
||||||
|
import Api.Model.Tag exposing (Tag)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
@ -30,42 +33,45 @@ type LinkTarget
|
|||||||
|
|
||||||
makeCorrLink :
|
makeCorrLink :
|
||||||
{ a | corrOrg : Maybe IdName, corrPerson : Maybe IdName }
|
{ a | corrOrg : Maybe IdName, corrPerson : Maybe IdName }
|
||||||
|
-> List ( String, Bool )
|
||||||
-> (LinkTarget -> msg)
|
-> (LinkTarget -> msg)
|
||||||
-> List (Html msg)
|
-> List (Html msg)
|
||||||
makeCorrLink item tagger =
|
makeCorrLink item linkClasses tagger =
|
||||||
let
|
let
|
||||||
makeOrg idname =
|
makeOrg idname =
|
||||||
makeLink [] (LinkCorrOrg >> tagger) idname
|
makeLink linkClasses (LinkCorrOrg >> tagger) idname
|
||||||
|
|
||||||
makePerson idname =
|
makePerson idname =
|
||||||
makeLink [] (LinkCorrPerson >> tagger) idname
|
makeLink linkClasses (LinkCorrPerson >> tagger) idname
|
||||||
in
|
in
|
||||||
combine (Maybe.map makeOrg item.corrOrg) (Maybe.map makePerson item.corrPerson)
|
combine (Maybe.map makeOrg item.corrOrg) (Maybe.map makePerson item.corrPerson)
|
||||||
|
|
||||||
|
|
||||||
makeConcLink :
|
makeConcLink :
|
||||||
{ a | concPerson : Maybe IdName, concEquipment : Maybe IdName }
|
{ a | concPerson : Maybe IdName, concEquipment : Maybe IdName }
|
||||||
|
-> List ( String, Bool )
|
||||||
-> (LinkTarget -> msg)
|
-> (LinkTarget -> msg)
|
||||||
-> List (Html msg)
|
-> List (Html msg)
|
||||||
makeConcLink item tagger =
|
makeConcLink item linkClasses tagger =
|
||||||
let
|
let
|
||||||
makePerson idname =
|
makePerson idname =
|
||||||
makeLink [] (LinkConcPerson >> tagger) idname
|
makeLink linkClasses (LinkConcPerson >> tagger) idname
|
||||||
|
|
||||||
makeEquip idname =
|
makeEquip idname =
|
||||||
makeLink [] (LinkConcEquip >> tagger) idname
|
makeLink linkClasses (LinkConcEquip >> tagger) idname
|
||||||
in
|
in
|
||||||
combine (Maybe.map makePerson item.concPerson) (Maybe.map makeEquip item.concEquipment)
|
combine (Maybe.map makePerson item.concPerson) (Maybe.map makeEquip item.concEquipment)
|
||||||
|
|
||||||
|
|
||||||
makeFolderLink :
|
makeFolderLink :
|
||||||
{ a | folder : Maybe IdName }
|
{ a | folder : Maybe IdName }
|
||||||
|
-> List ( String, Bool )
|
||||||
-> (LinkTarget -> msg)
|
-> (LinkTarget -> msg)
|
||||||
-> Html msg
|
-> Html msg
|
||||||
makeFolderLink item tagger =
|
makeFolderLink item linkClasses tagger =
|
||||||
let
|
let
|
||||||
makeFolder idname =
|
makeFolder idname =
|
||||||
makeLink [] (LinkFolder >> tagger) idname
|
makeLink linkClasses (LinkFolder >> tagger) idname
|
||||||
in
|
in
|
||||||
Maybe.map makeFolder item.folder
|
Maybe.map makeFolder item.folder
|
||||||
|> Maybe.withDefault (text "-")
|
|> Maybe.withDefault (text "-")
|
||||||
@ -77,7 +83,17 @@ makeTagLink :
|
|||||||
-> (LinkTarget -> msg)
|
-> (LinkTarget -> msg)
|
||||||
-> Html msg
|
-> Html msg
|
||||||
makeTagLink tagId classes tagger =
|
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 :
|
makeCustomFieldLink :
|
||||||
@ -92,6 +108,18 @@ makeCustomFieldLink cv classes tagger =
|
|||||||
cv
|
cv
|
||||||
|
|
||||||
|
|
||||||
|
makeCustomFieldLink2 :
|
||||||
|
ItemFieldValue
|
||||||
|
-> List ( String, Bool )
|
||||||
|
-> (LinkTarget -> msg)
|
||||||
|
-> Html msg
|
||||||
|
makeCustomFieldLink2 cv classes tagger =
|
||||||
|
Util.CustomField.renderValue2
|
||||||
|
classes
|
||||||
|
(tagger (LinkCustomField cv) |> Just)
|
||||||
|
cv
|
||||||
|
|
||||||
|
|
||||||
makeSourceLink :
|
makeSourceLink :
|
||||||
List ( String, Bool )
|
List ( String, Bool )
|
||||||
-> (LinkTarget -> msg)
|
-> (LinkTarget -> msg)
|
||||||
@ -130,3 +158,22 @@ makeLink classes tagger idname =
|
|||||||
]
|
]
|
||||||
[ text idname.name
|
[ 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
|
, update
|
||||||
, view
|
, view
|
||||||
, viewCheatLink
|
, viewCheatLink
|
||||||
|
, viewCheatLink2
|
||||||
, viewContent
|
, viewContent
|
||||||
|
, viewContent2
|
||||||
, viewEditLink
|
, viewEditLink
|
||||||
|
, viewEditLink2
|
||||||
, viewPreviewLink
|
, viewPreviewLink
|
||||||
|
, viewPreviewLink2
|
||||||
, viewSplitLink
|
, viewSplitLink
|
||||||
|
, viewSplitLink2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type Display
|
type Display
|
||||||
@ -51,6 +57,10 @@ update txt msg model =
|
|||||||
( { model | display = dsp }, txt )
|
( { model | display = dsp }, txt )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
viewContent : String -> Model -> Html Msg
|
viewContent : String -> Model -> Html Msg
|
||||||
viewContent txt model =
|
viewContent txt model =
|
||||||
case model.display of
|
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
|
, initWith
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -14,12 +15,15 @@ import Api.Model.EmailSettingsList exposing (EmailSettingsList)
|
|||||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
import Api.Model.TagList exposing (TagList)
|
import Api.Model.TagList exposing (TagList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.CalEventInput
|
import Comp.CalEventInput
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
import Comp.EmailInput
|
import Comp.EmailInput
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.CalEvent exposing (CalEvent)
|
import Data.CalEvent exposing (CalEvent)
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Data.Validated exposing (Validated(..))
|
import Data.Validated exposing (Validated(..))
|
||||||
@ -27,6 +31,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick)
|
import Html.Events exposing (onCheck, onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Tag
|
import Util.Tag
|
||||||
@ -142,8 +147,8 @@ init flags =
|
|||||||
{ makeOption = \a -> { value = a, text = a, additional = "" }
|
{ makeOption = \a -> { value = a, text = a, additional = "" }
|
||||||
, placeholder = "Select connection..."
|
, placeholder = "Select connection..."
|
||||||
}
|
}
|
||||||
, tagInclModel = Util.Tag.makeDropdownModel
|
, tagInclModel = Util.Tag.makeDropdownModel2
|
||||||
, tagExclModel = Util.Tag.makeDropdownModel
|
, tagExclModel = Util.Tag.makeDropdownModel2
|
||||||
, recipients = []
|
, recipients = []
|
||||||
, recipientsModel = Comp.EmailInput.init
|
, recipientsModel = Comp.EmailInput.init
|
||||||
, remindDays = Just 1
|
, remindDays = Just 1
|
||||||
@ -622,3 +627,185 @@ view extraClasses settings model =
|
|||||||
[ text "Start Once"
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Html
|
import Util.Html
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +42,10 @@ update msg model =
|
|||||||
( model, EditAction settings )
|
( model, EditAction settings )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> List NotificationSettings -> Html Msg
|
view : Model -> List NotificationSettings -> Html Msg
|
||||||
view _ items =
|
view _ items =
|
||||||
div []
|
div []
|
||||||
@ -93,3 +100,57 @@ viewItem item =
|
|||||||
|> text
|
|> 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
import Api.Model.NotificationSettings exposing (NotificationSettings)
|
||||||
import Api.Model.NotificationSettingsList exposing (NotificationSettingsList)
|
import Api.Model.NotificationSettingsList exposing (NotificationSettingsList)
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.NotificationForm
|
import Comp.NotificationForm
|
||||||
import Comp.NotificationList
|
import Comp.NotificationList
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
@ -18,6 +20,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -253,3 +256,57 @@ viewForm settings model =
|
|||||||
viewList : Model -> Html Msg
|
viewList : Model -> Html Msg
|
||||||
viewList model =
|
viewList model =
|
||||||
Html.map ListMsg (Comp.NotificationList.view model.listModel model.items)
|
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
|
, update
|
||||||
, view
|
, view
|
||||||
, view1
|
, view1
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Organization exposing (Organization)
|
import Api.Model.Organization exposing (Organization)
|
||||||
import Comp.AddressForm
|
import Comp.AddressForm
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.ContactField
|
import Comp.ContactField
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.Organization
|
import Api.Model.Organization
|
||||||
import Api.Model.OrganizationList exposing (OrganizationList)
|
import Api.Model.OrganizationList exposing (OrganizationList)
|
||||||
|
import Comp.Basic as B
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.OrgForm
|
import Comp.OrgForm
|
||||||
import Comp.OrgTable
|
import Comp.OrgTable
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -19,6 +22,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -301,3 +305,117 @@ viewForm settings model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Organization exposing (Organization)
|
import Api.Model.Organization exposing (Organization)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Address
|
import Util.Address
|
||||||
import Util.Contact
|
import Util.Contact
|
||||||
|
|
||||||
@ -88,3 +91,42 @@ renderOrgLine model org =
|
|||||||
[ Util.Contact.toString org.contacts |> text
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
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
|
, update
|
||||||
, view
|
, view
|
||||||
, view1
|
, view1
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.IdName exposing (IdName)
|
import Api.Model.IdName exposing (IdName)
|
||||||
import Api.Model.Person exposing (Person)
|
import Api.Model.Person exposing (Person)
|
||||||
import Comp.AddressForm
|
import Comp.AddressForm
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.ContactField
|
import Comp.ContactField
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onInput)
|
import Html.Events exposing (onCheck, onInput)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -11,6 +12,8 @@ import Api.Model.BasicResult exposing (BasicResult)
|
|||||||
import Api.Model.Person
|
import Api.Model.Person
|
||||||
import Api.Model.PersonList exposing (PersonList)
|
import Api.Model.PersonList exposing (PersonList)
|
||||||
import Api.Model.ReferenceList exposing (ReferenceList)
|
import Api.Model.ReferenceList exposing (ReferenceList)
|
||||||
|
import Comp.Basic as B
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.PersonForm
|
import Comp.PersonForm
|
||||||
import Comp.PersonTable
|
import Comp.PersonTable
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -20,6 +23,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -349,3 +353,117 @@ viewForm settings model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Person exposing (Person)
|
import Api.Model.Person exposing (Person)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Address
|
import Util.Address
|
||||||
import Util.Contact
|
import Util.Contact
|
||||||
|
import Util.Html
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -102,3 +106,50 @@ renderPersonLine model person =
|
|||||||
[ Util.Contact.toString person.contacts |> text
|
[ 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
|
module Comp.Progress exposing
|
||||||
( smallIndicating
|
( progress2
|
||||||
|
, smallIndicating
|
||||||
, topAttachedIndicating
|
, topAttachedIndicating
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -7,6 +8,21 @@ import Html exposing (Html, div, text)
|
|||||||
import Html.Attributes exposing (attribute, class, style)
|
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 : Int -> Html msg
|
||||||
smallIndicating percent =
|
smallIndicating percent =
|
||||||
progress "small indicating active" percent Nothing Nothing
|
progress "small indicating active" percent Nothing Nothing
|
||||||
|
@ -6,6 +6,7 @@ module Comp.ScanMailboxForm exposing
|
|||||||
, initWith
|
, initWith
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -18,14 +19,18 @@ import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
|||||||
import Api.Model.StringList exposing (StringList)
|
import Api.Model.StringList exposing (StringList)
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
import Api.Model.TagList exposing (TagList)
|
import Api.Model.TagList exposing (TagList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.CalEventInput
|
import Comp.CalEventInput
|
||||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.StringListInput
|
import Comp.StringListInput
|
||||||
|
import Comp.Tabs
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
import Data.CalEvent exposing (CalEvent)
|
import Data.CalEvent exposing (CalEvent)
|
||||||
import Data.Direction exposing (Direction(..))
|
import Data.Direction exposing (Direction(..))
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Language exposing (Language)
|
import Data.Language exposing (Language)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -35,6 +40,8 @@ import Html.Attributes exposing (..)
|
|||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Set exposing (Set)
|
||||||
|
import Styles as S
|
||||||
import Util.Folder exposing (mkFolderOption)
|
import Util.Folder exposing (mkFolderOption)
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.List
|
import Util.List
|
||||||
@ -70,6 +77,7 @@ type alias Model =
|
|||||||
, language : Maybe Language
|
, language : Maybe Language
|
||||||
, postHandleAll : Bool
|
, postHandleAll : Bool
|
||||||
, menuTab : MenuTab
|
, menuTab : MenuTab
|
||||||
|
, openTabs : Set String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,6 +123,7 @@ type Msg
|
|||||||
| RemoveLanguage
|
| RemoveLanguage
|
||||||
| TogglePostHandleAll
|
| TogglePostHandleAll
|
||||||
| SetMenuTab MenuTab
|
| SetMenuTab MenuTab
|
||||||
|
| ToggleAkkordionTab String
|
||||||
|
|
||||||
|
|
||||||
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
|
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
|
||||||
@ -217,6 +226,7 @@ init flags =
|
|||||||
, language = Nothing
|
, language = Nothing
|
||||||
, postHandleAll = False
|
, postHandleAll = False
|
||||||
, menuTab = TabGeneral
|
, menuTab = TabGeneral
|
||||||
|
, openTabs = Set.insert (tabTitle TabGeneral) Set.empty
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Api.getImapSettings flags "" ConnResp
|
[ Api.getImapSettings flags "" ConnResp
|
||||||
@ -676,6 +686,20 @@ update flags msg model =
|
|||||||
, Cmd.none
|
, 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
|
--- View
|
||||||
@ -694,6 +718,21 @@ isFormSuccess model =
|
|||||||
|> Maybe.withDefault False
|
|> 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 : String -> UiSettings -> Model -> Html Msg
|
||||||
view extraClasses settings model =
|
view extraClasses settings model =
|
||||||
div
|
div
|
||||||
@ -1106,12 +1145,455 @@ viewSchedule model =
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
isFolderMember : Model -> Bool
|
|
||||||
isFolderMember model =
|
--- View2
|
||||||
|
|
||||||
|
|
||||||
|
view2 : String -> UiSettings -> Model -> Html Msg
|
||||||
|
view2 extraClasses settings model =
|
||||||
let
|
let
|
||||||
selected =
|
dimmerSettings =
|
||||||
Comp.Dropdown.getSelected model.folderModel
|
Comp.YesNoDimmer.defaultSettings2 "Really delete this scan mailbox task?"
|
||||||
|> List.head
|
|
||||||
|> Maybe.map .id
|
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
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Html
|
import Util.Html
|
||||||
|
|
||||||
|
|
||||||
@ -39,6 +42,10 @@ update msg model =
|
|||||||
( model, EditAction settings )
|
( model, EditAction settings )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> List ScanMailboxSettings -> Html Msg
|
view : Model -> List ScanMailboxSettings -> Html Msg
|
||||||
view _ items =
|
view _ items =
|
||||||
div []
|
div []
|
||||||
@ -104,3 +111,65 @@ viewItem item =
|
|||||||
[ Util.Html.checkbox item.deleteMail
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
||||||
import Api.Model.ScanMailboxSettingsList exposing (ScanMailboxSettingsList)
|
import Api.Model.ScanMailboxSettingsList exposing (ScanMailboxSettingsList)
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.ScanMailboxForm
|
import Comp.ScanMailboxForm
|
||||||
import Comp.ScanMailboxList
|
import Comp.ScanMailboxList
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
@ -18,6 +20,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
|
|
||||||
|
|
||||||
@ -253,3 +256,57 @@ viewForm settings model =
|
|||||||
viewList : Model -> Html Msg
|
viewList : Model -> Html Msg
|
||||||
viewList model =
|
viewList model =
|
||||||
Html.map ListMsg (Comp.ScanMailboxList.view model.listModel model.items)
|
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
|
, updateDrop
|
||||||
, view
|
, view
|
||||||
, viewDrop
|
, viewDrop
|
||||||
|
, viewDrop2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -24,14 +25,16 @@ import Api.Model.ItemSearch exposing (ItemSearch)
|
|||||||
import Api.Model.PersonList exposing (PersonList)
|
import Api.Model.PersonList exposing (PersonList)
|
||||||
import Api.Model.ReferenceList exposing (ReferenceList)
|
import Api.Model.ReferenceList exposing (ReferenceList)
|
||||||
import Api.Model.SearchStats exposing (SearchStats)
|
import Api.Model.SearchStats exposing (SearchStats)
|
||||||
import Api.Model.TagList exposing (TagList)
|
|
||||||
import Comp.CustomFieldMultiInput
|
import Comp.CustomFieldMultiInput
|
||||||
import Comp.DatePicker
|
import Comp.DatePicker
|
||||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
||||||
import Comp.FolderSelect
|
import Comp.FolderSelect
|
||||||
|
import Comp.MenuBar as MB
|
||||||
|
import Comp.Tabs
|
||||||
import Comp.TagSelect
|
import Comp.TagSelect
|
||||||
import Data.CustomFieldChange exposing (CustomFieldValueCollect)
|
import Data.CustomFieldChange exposing (CustomFieldValueCollect)
|
||||||
import Data.Direction exposing (Direction)
|
import Data.Direction exposing (Direction)
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Fields
|
import Data.Fields
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Icons as Icons
|
import Data.Icons as Icons
|
||||||
@ -41,6 +44,8 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
|
import Set exposing (Set)
|
||||||
|
import Styles as S
|
||||||
import Util.Html exposing (KeyCode(..))
|
import Util.Html exposing (KeyCode(..))
|
||||||
import Util.ItemDragDrop as DD
|
import Util.ItemDragDrop as DD
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
@ -75,6 +80,7 @@ type alias Model =
|
|||||||
, customFieldModel : Comp.CustomFieldMultiInput.Model
|
, customFieldModel : Comp.CustomFieldMultiInput.Model
|
||||||
, customValues : CustomFieldValueCollect
|
, customValues : CustomFieldValueCollect
|
||||||
, sourceModel : Maybe String
|
, sourceModel : Maybe String
|
||||||
|
, openTabs : Set String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -141,6 +147,7 @@ init flags =
|
|||||||
, customFieldModel = Comp.CustomFieldMultiInput.initWith []
|
, customFieldModel = Comp.CustomFieldMultiInput.initWith []
|
||||||
, customValues = Data.CustomFieldChange.emptyCollect
|
, customValues = Data.CustomFieldChange.emptyCollect
|
||||||
, sourceModel = Nothing
|
, sourceModel = Nothing
|
||||||
|
, openTabs = Set.fromList [ "Tags", "Inbox" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -361,6 +368,8 @@ type Msg
|
|||||||
| ResetToSource String
|
| ResetToSource String
|
||||||
| GetStatsResp (Result Http.Error SearchStats)
|
| GetStatsResp (Result Http.Error SearchStats)
|
||||||
| GetAllTagsResp (Result Http.Error SearchStats)
|
| GetAllTagsResp (Result Http.Error SearchStats)
|
||||||
|
| ToggleAkkordionTab String
|
||||||
|
| ToggleOpenAllAkkordionTabs
|
||||||
|
|
||||||
|
|
||||||
type alias NextState =
|
type alias NextState =
|
||||||
@ -838,7 +847,7 @@ updateDrop ddm flags settings msg model =
|
|||||||
CustomFieldMsg lm ->
|
CustomFieldMsg lm ->
|
||||||
let
|
let
|
||||||
res =
|
res =
|
||||||
Comp.CustomFieldMultiInput.updateSearch lm model.customFieldModel
|
Comp.CustomFieldMultiInput.updateSearch flags lm model.customFieldModel
|
||||||
in
|
in
|
||||||
{ model =
|
{ model =
|
||||||
{ model
|
{ model
|
||||||
@ -881,6 +890,41 @@ updateDrop ddm flags settings msg model =
|
|||||||
ResetToSource str ->
|
ResetToSource str ->
|
||||||
resetAndSet (SetSource 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
|
-- 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
|
( nameOrLabel
|
||||||
, sortFields
|
, sortFields
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.FieldStats exposing (FieldStats)
|
import Api.Model.FieldStats exposing (FieldStats)
|
||||||
import Api.Model.SearchStats exposing (SearchStats)
|
import Api.Model.SearchStats exposing (SearchStats)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Icons as Icons
|
import Data.Icons as Icons
|
||||||
import Data.Money
|
import Data.Money
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
nameOrLabel : FieldStats -> String
|
nameOrLabel : FieldStats -> String
|
||||||
@ -17,6 +20,15 @@ nameOrLabel f =
|
|||||||
Maybe.withDefault f.name f.label
|
Maybe.withDefault f.name f.label
|
||||||
|
|
||||||
|
|
||||||
|
sortFields : List FieldStats -> List FieldStats
|
||||||
|
sortFields fields =
|
||||||
|
List.sortBy nameOrLabel fields
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : SearchStats -> List (Html msg)
|
view : SearchStats -> List (Html msg)
|
||||||
view stats =
|
view stats =
|
||||||
let
|
let
|
||||||
@ -85,6 +97,80 @@ view stats =
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
sortFields : List FieldStats -> List FieldStats
|
|
||||||
sortFields fields =
|
--- View2
|
||||||
List.sortBy nameOrLabel fields
|
|
||||||
|
|
||||||
|
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
|
, isEmpty
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.SentMail exposing (SentMail)
|
import Api.Model.SentMail exposing (SentMail)
|
||||||
|
import Comp.Basic as B
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Time
|
import Util.Time
|
||||||
|
|
||||||
|
|
||||||
@ -53,6 +56,10 @@ update msg model =
|
|||||||
{ model | selected = Just m }
|
{ model | selected = Just m }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
case model.selected of
|
case model.selected of
|
||||||
@ -129,3 +136,105 @@ renderLine mail =
|
|||||||
]
|
]
|
||||||
, td [ class "collapsing" ] [ text mail.sender ]
|
, 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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -15,8 +16,10 @@ import Api.Model.IdName exposing (IdName)
|
|||||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
import Api.Model.TagList exposing (TagList)
|
import Api.Model.TagList exposing (TagList)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
||||||
import Comp.FixedDropdown
|
import Comp.FixedDropdown
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Priority exposing (Priority)
|
import Data.Priority exposing (Priority)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -25,6 +28,7 @@ import Html.Attributes exposing (..)
|
|||||||
import Html.Events exposing (onCheck, onInput)
|
import Html.Events exposing (onCheck, onInput)
|
||||||
import Http
|
import Http
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Styles as S
|
||||||
import Util.Folder exposing (mkFolderOption)
|
import Util.Folder exposing (mkFolderOption)
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Tag
|
import Util.Tag
|
||||||
@ -64,7 +68,7 @@ emptyModel =
|
|||||||
}
|
}
|
||||||
, allFolders = []
|
, allFolders = []
|
||||||
, folderId = Nothing
|
, folderId = Nothing
|
||||||
, tagModel = Util.Tag.makeDropdownModel
|
, tagModel = Util.Tag.makeDropdownModel2
|
||||||
, fileFilter = Nothing
|
, fileFilter = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,3 +419,177 @@ isFolderMember model =
|
|||||||
|> Maybe.map .id
|
|> Maybe.map .id
|
||||||
in
|
in
|
||||||
Util.Folder.isFolderMember model.allFolders selected
|
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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
||||||
import Api.Model.SourceList exposing (SourceList)
|
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.SourceForm
|
||||||
import Comp.SourceTable exposing (SelectMode(..))
|
import Comp.SourceTable exposing (SelectMode(..))
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -22,6 +24,7 @@ import Html.Events exposing (onClick, onSubmit)
|
|||||||
import Http
|
import Http
|
||||||
import Ports
|
import Ports
|
||||||
import QRCode
|
import QRCode
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
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
|
, isEdit
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Priority
|
import Data.Priority
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
import Util.Html
|
||||||
|
|
||||||
|
|
||||||
type SelectMode
|
type SelectMode
|
||||||
@ -48,6 +52,10 @@ update _ msg =
|
|||||||
( Cmd.none, Display source )
|
( Cmd.none, Display source )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : List SourceAndTags -> Html Msg
|
view : List SourceAndTags -> Html Msg
|
||||||
view sources =
|
view sources =
|
||||||
table [ class "ui table" ]
|
table [ class "ui table" ]
|
||||||
@ -115,3 +123,63 @@ renderSourceLine source =
|
|||||||
[ text source.source.id
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput)
|
import Html.Events exposing (onClick, onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -137,3 +141,45 @@ view model =
|
|||||||
, Html.map CatMsg (Comp.Dropdown.viewSingle model.catDropdown)
|
, 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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.Tag
|
import Api.Model.Tag
|
||||||
import Api.Model.TagList exposing (TagList)
|
import Api.Model.TagList exposing (TagList)
|
||||||
|
import Comp.Basic as B
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.TagForm
|
import Comp.TagForm
|
||||||
import Comp.TagTable
|
import Comp.TagTable
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -18,6 +21,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onInput, onSubmit)
|
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Tag
|
import Util.Tag
|
||||||
@ -52,6 +56,11 @@ emptyModel =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dimmerSettings : Comp.YesNoDimmer.Settings
|
||||||
|
dimmerSettings =
|
||||||
|
Comp.YesNoDimmer.defaultSettings
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= TableMsg Comp.TagTable.Msg
|
= TableMsg Comp.TagTable.Msg
|
||||||
| FormMsg Comp.TagForm.Msg
|
| FormMsg Comp.TagForm.Msg
|
||||||
@ -309,3 +318,120 @@ viewForm model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, Model
|
||||||
, Msg
|
, Msg
|
||||||
, Selection
|
, Selection
|
||||||
|
, WorkModel
|
||||||
, emptySelection
|
, emptySelection
|
||||||
, init
|
, init
|
||||||
|
, makeWorkModel
|
||||||
, modifyAll
|
, modifyAll
|
||||||
, modifyCount
|
, modifyCount
|
||||||
, reset
|
, reset
|
||||||
@ -12,6 +14,9 @@ module Comp.TagSelect exposing
|
|||||||
, update
|
, update
|
||||||
, updateDrop
|
, updateDrop
|
||||||
, viewAll
|
, viewAll
|
||||||
|
, viewAll2
|
||||||
|
, viewCats2
|
||||||
|
, viewTagsDrop2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
@ -25,6 +30,7 @@ import Html.Events exposing (onClick, onInput)
|
|||||||
import Set
|
import Set
|
||||||
import Simple.Fuzzy
|
import Simple.Fuzzy
|
||||||
import String as S
|
import String as S
|
||||||
|
import Styles as S
|
||||||
import Util.ExpandCollapse
|
import Util.ExpandCollapse
|
||||||
import Util.ItemDragDrop as DD
|
import Util.ItemDragDrop as DD
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
@ -413,6 +419,10 @@ catState model name =
|
|||||||
Deselect
|
Deselect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
viewAll : DD.Model -> UiSettings -> Selection -> Model -> List (Html Msg)
|
viewAll : DD.Model -> UiSettings -> Selection -> Model -> List (Html Msg)
|
||||||
viewAll ddm settings sel model =
|
viewAll ddm settings sel model =
|
||||||
let
|
let
|
||||||
@ -630,3 +640,219 @@ getIcon state color default =
|
|||||||
|
|
||||||
Deselect ->
|
Deselect ->
|
||||||
default color
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -63,8 +66,7 @@ view model =
|
|||||||
renderTagLine : Model -> Tag -> Html Msg
|
renderTagLine : Model -> Tag -> Html Msg
|
||||||
renderTagLine model tag =
|
renderTagLine model tag =
|
||||||
tr
|
tr
|
||||||
[ classList [ ( "active", model.selected == Just tag ) ]
|
[]
|
||||||
]
|
|
||||||
[ td [ class "collapsing" ]
|
[ td [ class "collapsing" ]
|
||||||
[ a
|
[ a
|
||||||
[ href "#"
|
[ href "#"
|
||||||
@ -82,3 +84,38 @@ renderTagLine model tag =
|
|||||||
[ Maybe.withDefault "-" tag.category |> text
|
[ 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
@ -12,6 +13,8 @@ import Comp.BasicSizeField
|
|||||||
import Comp.ColorTagger
|
import Comp.ColorTagger
|
||||||
import Comp.FieldListSelect
|
import Comp.FieldListSelect
|
||||||
import Comp.IntField
|
import Comp.IntField
|
||||||
|
import Comp.MenuBar as MB
|
||||||
|
import Comp.Tabs
|
||||||
import Data.BasicSize exposing (BasicSize)
|
import Data.BasicSize exposing (BasicSize)
|
||||||
import Data.Color exposing (Color)
|
import Data.Color exposing (Color)
|
||||||
import Data.Fields exposing (Field)
|
import Data.Fields exposing (Field)
|
||||||
@ -24,6 +27,8 @@ import Html.Attributes exposing (..)
|
|||||||
import Html.Events exposing (onCheck, onClick, onInput)
|
import Html.Events exposing (onCheck, onClick, onInput)
|
||||||
import Http
|
import Http
|
||||||
import Markdown
|
import Markdown
|
||||||
|
import Set exposing (Set)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
import Util.Tag
|
import Util.Tag
|
||||||
|
|
||||||
@ -52,6 +57,8 @@ type alias Model =
|
|||||||
, cardSubtitlePattern : PatternModel
|
, cardSubtitlePattern : PatternModel
|
||||||
, showPatternHelp : Bool
|
, showPatternHelp : Bool
|
||||||
, searchStatsVisible : Bool
|
, searchStatsVisible : Bool
|
||||||
|
, sideMenuVisible : Bool
|
||||||
|
, openTabs : Set String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -143,6 +150,8 @@ init flags settings =
|
|||||||
, cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
|
, cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
|
||||||
, showPatternHelp = False
|
, showPatternHelp = False
|
||||||
, searchStatsVisible = settings.searchStatsVisible
|
, searchStatsVisible = settings.searchStatsVisible
|
||||||
|
, sideMenuVisible = settings.sideMenuVisible
|
||||||
|
, openTabs = Set.empty
|
||||||
}
|
}
|
||||||
, Api.getTags flags "" GetTagsResp
|
, Api.getTags flags "" GetTagsResp
|
||||||
)
|
)
|
||||||
@ -167,6 +176,8 @@ type Msg
|
|||||||
| SetCardSubtitlePattern String
|
| SetCardSubtitlePattern String
|
||||||
| TogglePatternHelpMsg
|
| TogglePatternHelpMsg
|
||||||
| ToggleSearchStatsVisible
|
| ToggleSearchStatsVisible
|
||||||
|
| ToggleAkkordionTab String
|
||||||
|
| ToggleSideMenuVisible
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -427,6 +438,28 @@ update sett msg model =
|
|||||||
, Just { sett | searchStatsVisible = flag }
|
, 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
|
--- View
|
||||||
@ -655,3 +688,231 @@ view flags _ model =
|
|||||||
]
|
]
|
||||||
, Html.map FieldListMsg (Comp.FieldListSelect.view model.formFields)
|
, 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
|
, init
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.UiSettingsForm
|
import Comp.UiSettingsForm
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -14,6 +16,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Ports
|
import Ports
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -147,3 +150,42 @@ view flags settings classes model =
|
|||||||
|> text
|
|> 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
|
, isValid
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.User exposing (User)
|
import Api.Model.User exposing (User)
|
||||||
|
import Comp.Basic as B
|
||||||
import Comp.Dropdown
|
import Comp.Dropdown
|
||||||
|
import Data.DropdownStyle as DS
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Data.UserState exposing (UserState)
|
import Data.UserState exposing (UserState)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput)
|
import Html.Events exposing (onInput)
|
||||||
|
import Styles as S
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +163,10 @@ update _ msg model =
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
div [ class "ui form" ]
|
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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
import Api.Model.User
|
import Api.Model.User
|
||||||
import Api.Model.UserList exposing (UserList)
|
import Api.Model.UserList exposing (UserList)
|
||||||
|
import Comp.Basic as B
|
||||||
|
import Comp.MenuBar as MB
|
||||||
import Comp.UserForm
|
import Comp.UserForm
|
||||||
import Comp.UserTable
|
import Comp.UserTable
|
||||||
import Comp.YesNoDimmer
|
import Comp.YesNoDimmer
|
||||||
@ -19,6 +22,7 @@ import Html exposing (..)
|
|||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick, onSubmit)
|
import Html.Events exposing (onClick, onSubmit)
|
||||||
import Http
|
import Http
|
||||||
|
import Styles as S
|
||||||
import Util.Http
|
import Util.Http
|
||||||
import Util.Maybe
|
import Util.Maybe
|
||||||
|
|
||||||
@ -195,6 +199,10 @@ update flags msg model =
|
|||||||
( { model | deleteConfirm = cm }, cmd )
|
( { model | deleteConfirm = cm }, cmd )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : UiSettings -> Model -> Html Msg
|
view : UiSettings -> Model -> Html Msg
|
||||||
view settings model =
|
view settings model =
|
||||||
if model.viewMode == Table then
|
if model.viewMode == Table then
|
||||||
@ -271,3 +279,106 @@ viewForm settings model =
|
|||||||
[ div [ class "ui loader" ] []
|
[ 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
|
, emptyModel
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, view2
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.User exposing (User)
|
import Api.Model.User exposing (User)
|
||||||
|
import Comp.Basic as B
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
import Util.Time exposing (formatDateTime)
|
import Util.Time exposing (formatDateTime)
|
||||||
|
|
||||||
|
|
||||||
@ -46,6 +49,10 @@ update _ msg model =
|
|||||||
( { model | selected = Nothing }, Cmd.none )
|
( { model | selected = Nothing }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- View
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
table [ class "ui selectable table" ]
|
table [ class "ui selectable table" ]
|
||||||
@ -89,3 +96,54 @@ renderUserLine model user =
|
|||||||
[ formatDateTime user.created |> text
|
[ 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
|
, Settings
|
||||||
, activate
|
, activate
|
||||||
, defaultSettings
|
, defaultSettings
|
||||||
|
, defaultSettings2
|
||||||
, disable
|
, disable
|
||||||
, emptyModel
|
, emptyModel
|
||||||
, initActive
|
, initActive
|
||||||
@ -11,11 +12,13 @@ module Comp.YesNoDimmer exposing
|
|||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
, view2
|
, view2
|
||||||
|
, viewN
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
import Styles as S
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
@ -53,7 +56,6 @@ type alias Settings =
|
|||||||
, headerClass : String
|
, headerClass : String
|
||||||
, confirmButton : String
|
, confirmButton : String
|
||||||
, cancelButton : String
|
, cancelButton : String
|
||||||
, invertedDimmer : Bool
|
|
||||||
, extraClass : String
|
, extraClass : String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +67,17 @@ defaultSettings =
|
|||||||
, headerClass = "ui inverted icon header"
|
, headerClass = "ui inverted icon header"
|
||||||
, confirmButton = "Yes, do it!"
|
, confirmButton = "Yes, do it!"
|
||||||
, cancelButton = "No"
|
, 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 = ""
|
, extraClass = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +116,6 @@ view2 active settings model =
|
|||||||
[ classList
|
[ classList
|
||||||
[ ( "ui dimmer", True )
|
[ ( "ui dimmer", True )
|
||||||
, ( settings.extraClass, True )
|
, ( settings.extraClass, True )
|
||||||
, ( "inverted", settings.invertedDimmer )
|
|
||||||
, ( "active", active && model.active )
|
, ( "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
Loading…
x
Reference in New Issue
Block a user