mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-11-03 18:00:11 +00:00 
			
		
		
		
	Manage custom fields in webui
This commit is contained in:
		@@ -19,6 +19,7 @@ module Api exposing
 | 
			
		||||
    , createScanMailbox
 | 
			
		||||
    , deleteAllItems
 | 
			
		||||
    , deleteAttachment
 | 
			
		||||
    , deleteCustomField
 | 
			
		||||
    , deleteEquip
 | 
			
		||||
    , deleteFolder
 | 
			
		||||
    , deleteImapSettings
 | 
			
		||||
@@ -36,6 +37,7 @@ module Api exposing
 | 
			
		||||
    , getCollective
 | 
			
		||||
    , getCollectiveSettings
 | 
			
		||||
    , getContacts
 | 
			
		||||
    , getCustomFields
 | 
			
		||||
    , getEquipment
 | 
			
		||||
    , getEquipments
 | 
			
		||||
    , getFolderDetail
 | 
			
		||||
@@ -68,12 +70,14 @@ module Api exposing
 | 
			
		||||
    , logout
 | 
			
		||||
    , moveAttachmentBefore
 | 
			
		||||
    , newInvite
 | 
			
		||||
    , postCustomField
 | 
			
		||||
    , postEquipment
 | 
			
		||||
    , postNewUser
 | 
			
		||||
    , postOrg
 | 
			
		||||
    , postPerson
 | 
			
		||||
    , postSource
 | 
			
		||||
    , postTag
 | 
			
		||||
    , putCustomField
 | 
			
		||||
    , putUser
 | 
			
		||||
    , refreshSession
 | 
			
		||||
    , register
 | 
			
		||||
@@ -129,6 +133,7 @@ import Api.Model.CalEventCheckResult exposing (CalEventCheckResult)
 | 
			
		||||
import Api.Model.Collective exposing (Collective)
 | 
			
		||||
import Api.Model.CollectiveSettings exposing (CollectiveSettings)
 | 
			
		||||
import Api.Model.ContactList exposing (ContactList)
 | 
			
		||||
import Api.Model.CustomFieldList exposing (CustomFieldList)
 | 
			
		||||
import Api.Model.DirectionValue exposing (DirectionValue)
 | 
			
		||||
import Api.Model.EmailSettings exposing (EmailSettings)
 | 
			
		||||
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
 | 
			
		||||
@@ -145,7 +150,6 @@ import Api.Model.InviteResult exposing (InviteResult)
 | 
			
		||||
import Api.Model.ItemDetail exposing (ItemDetail)
 | 
			
		||||
import Api.Model.ItemFtsSearch exposing (ItemFtsSearch)
 | 
			
		||||
import Api.Model.ItemInsights exposing (ItemInsights)
 | 
			
		||||
import Api.Model.ItemLight exposing (ItemLight)
 | 
			
		||||
import Api.Model.ItemLightList exposing (ItemLightList)
 | 
			
		||||
import Api.Model.ItemProposals exposing (ItemProposals)
 | 
			
		||||
import Api.Model.ItemSearch exposing (ItemSearch)
 | 
			
		||||
@@ -158,6 +162,7 @@ import Api.Model.ItemsAndRefs exposing (ItemsAndRefs)
 | 
			
		||||
import Api.Model.JobPriority exposing (JobPriority)
 | 
			
		||||
import Api.Model.JobQueueState exposing (JobQueueState)
 | 
			
		||||
import Api.Model.MoveAttachment exposing (MoveAttachment)
 | 
			
		||||
import Api.Model.NewCustomField exposing (NewCustomField)
 | 
			
		||||
import Api.Model.NewFolder exposing (NewFolder)
 | 
			
		||||
import Api.Model.NotificationSettings exposing (NotificationSettings)
 | 
			
		||||
import Api.Model.NotificationSettingsList exposing (NotificationSettingsList)
 | 
			
		||||
@@ -177,7 +182,7 @@ import Api.Model.SentMails exposing (SentMails)
 | 
			
		||||
import Api.Model.SimpleMail exposing (SimpleMail)
 | 
			
		||||
import Api.Model.SourceAndTags exposing (SourceAndTags)
 | 
			
		||||
import Api.Model.SourceList exposing (SourceList)
 | 
			
		||||
import Api.Model.SourceTagIn exposing (SourceTagIn)
 | 
			
		||||
import Api.Model.SourceTagIn
 | 
			
		||||
import Api.Model.StringList exposing (StringList)
 | 
			
		||||
import Api.Model.Tag exposing (Tag)
 | 
			
		||||
import Api.Model.TagCloud exposing (TagCloud)
 | 
			
		||||
@@ -200,6 +205,60 @@ import Util.Http as Http2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Custom Fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
getCustomFields : Flags -> String -> (Result Http.Error CustomFieldList -> msg) -> Cmd msg
 | 
			
		||||
getCustomFields flags query receive =
 | 
			
		||||
    Http2.authGet
 | 
			
		||||
        { url =
 | 
			
		||||
            flags.config.baseUrl
 | 
			
		||||
                ++ "/api/v1/sec/customfield?q="
 | 
			
		||||
                ++ Url.percentEncode query
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.CustomFieldList.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
postCustomField :
 | 
			
		||||
    Flags
 | 
			
		||||
    -> NewCustomField
 | 
			
		||||
    -> (Result Http.Error BasicResult -> msg)
 | 
			
		||||
    -> Cmd msg
 | 
			
		||||
postCustomField flags field receive =
 | 
			
		||||
    Http2.authPost
 | 
			
		||||
        { url = flags.config.baseUrl ++ "/api/v1/sec/customfield"
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , body = Http.jsonBody (Api.Model.NewCustomField.encode field)
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
putCustomField :
 | 
			
		||||
    Flags
 | 
			
		||||
    -> String
 | 
			
		||||
    -> NewCustomField
 | 
			
		||||
    -> (Result Http.Error BasicResult -> msg)
 | 
			
		||||
    -> Cmd msg
 | 
			
		||||
putCustomField flags id field receive =
 | 
			
		||||
    Http2.authPut
 | 
			
		||||
        { url = flags.config.baseUrl ++ "/api/v1/sec/customfield/" ++ id
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , body = Http.jsonBody (Api.Model.NewCustomField.encode field)
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
deleteCustomField : Flags -> String -> (Result Http.Error BasicResult -> msg) -> Cmd msg
 | 
			
		||||
deleteCustomField flags id receive =
 | 
			
		||||
    Http2.authDelete
 | 
			
		||||
        { url = flags.config.baseUrl ++ "/api/v1/sec/customfield/" ++ id
 | 
			
		||||
        , account = getAccount flags
 | 
			
		||||
        , expect = Http.expectJson receive Api.Model.BasicResult.decoder
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Folders
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										284
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldDetail.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldDetail.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
			
		||||
module Comp.CustomFieldDetail exposing
 | 
			
		||||
    ( Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , init
 | 
			
		||||
    , initEmpty
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.BasicResult exposing (BasicResult)
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.NewCustomField exposing (NewCustomField)
 | 
			
		||||
import Comp.FixedDropdown
 | 
			
		||||
import Comp.YesNoDimmer
 | 
			
		||||
import Data.CustomFieldType exposing (CustomFieldType)
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Data.Validated exposing (Validated)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
import Util.Http
 | 
			
		||||
import Util.Maybe
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
    { result : Maybe BasicResult
 | 
			
		||||
    , field : CustomField
 | 
			
		||||
    , name : Maybe String
 | 
			
		||||
    , label : Maybe String
 | 
			
		||||
    , ftype : Maybe CustomFieldType
 | 
			
		||||
    , ftypeModel : Comp.FixedDropdown.Model CustomFieldType
 | 
			
		||||
    , loading : Bool
 | 
			
		||||
    , deleteDimmer : Comp.YesNoDimmer.Model
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
    = SetName String
 | 
			
		||||
    | SetLabel String
 | 
			
		||||
    | FTypeMsg (Comp.FixedDropdown.Msg CustomFieldType)
 | 
			
		||||
    | RequestDelete
 | 
			
		||||
    | DeleteMsg Comp.YesNoDimmer.Msg
 | 
			
		||||
    | UpdateResp (Result Http.Error BasicResult)
 | 
			
		||||
    | GoBack
 | 
			
		||||
    | SubmitForm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
init : CustomField -> Model
 | 
			
		||||
init field =
 | 
			
		||||
    { result = Nothing
 | 
			
		||||
    , field = field
 | 
			
		||||
    , name = Util.Maybe.fromString field.name
 | 
			
		||||
    , label = field.label
 | 
			
		||||
    , ftype = Data.CustomFieldType.fromString field.ftype
 | 
			
		||||
    , ftypeModel =
 | 
			
		||||
        Comp.FixedDropdown.initMap Data.CustomFieldType.label
 | 
			
		||||
            Data.CustomFieldType.all
 | 
			
		||||
    , loading = False
 | 
			
		||||
    , deleteDimmer = Comp.YesNoDimmer.emptyModel
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
initEmpty : Model
 | 
			
		||||
initEmpty =
 | 
			
		||||
    init Api.Model.CustomField.empty
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Update
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
makeField : Model -> Validated NewCustomField
 | 
			
		||||
makeField model =
 | 
			
		||||
    let
 | 
			
		||||
        name =
 | 
			
		||||
            Maybe.map Data.Validated.Valid model.name
 | 
			
		||||
                |> Maybe.withDefault (Data.Validated.Invalid [ "A name is required." ] "")
 | 
			
		||||
 | 
			
		||||
        ftype =
 | 
			
		||||
            Maybe.map Data.CustomFieldType.asString model.ftype
 | 
			
		||||
                |> Maybe.map Data.Validated.Valid
 | 
			
		||||
                |> Maybe.withDefault (Data.Validated.Invalid [ "A field type is required." ] "")
 | 
			
		||||
 | 
			
		||||
        make n ft =
 | 
			
		||||
            NewCustomField n model.label ft
 | 
			
		||||
    in
 | 
			
		||||
    Data.Validated.map2 make name ftype
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Bool )
 | 
			
		||||
update flags msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
        GoBack ->
 | 
			
		||||
            ( model, Cmd.none, True )
 | 
			
		||||
 | 
			
		||||
        FTypeMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                ( m2, sel ) =
 | 
			
		||||
                    Comp.FixedDropdown.update lm model.ftypeModel
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | ftype = Util.Maybe.or [ sel, model.ftype ], ftypeModel = m2 }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            , False
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        SetName str ->
 | 
			
		||||
            ( { model | name = Util.Maybe.fromString str }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            , False
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        SetLabel str ->
 | 
			
		||||
            ( { model | label = Util.Maybe.fromString str }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            , False
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        SubmitForm ->
 | 
			
		||||
            let
 | 
			
		||||
                newField =
 | 
			
		||||
                    makeField model
 | 
			
		||||
            in
 | 
			
		||||
            case newField of
 | 
			
		||||
                Data.Validated.Valid f ->
 | 
			
		||||
                    ( model
 | 
			
		||||
                    , if model.field.id == "" then
 | 
			
		||||
                        Api.postCustomField flags f UpdateResp
 | 
			
		||||
 | 
			
		||||
                      else
 | 
			
		||||
                        Api.putCustomField flags model.field.id f UpdateResp
 | 
			
		||||
                    , False
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Data.Validated.Invalid msgs _ ->
 | 
			
		||||
                    let
 | 
			
		||||
                        combined =
 | 
			
		||||
                            String.join "; " msgs
 | 
			
		||||
                    in
 | 
			
		||||
                    ( { model | result = Just (BasicResult False combined) }
 | 
			
		||||
                    , Cmd.none
 | 
			
		||||
                    , False
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Data.Validated.Unknown _ ->
 | 
			
		||||
                    ( model, Cmd.none, False )
 | 
			
		||||
 | 
			
		||||
        RequestDelete ->
 | 
			
		||||
            let
 | 
			
		||||
                ( dm, _ ) =
 | 
			
		||||
                    Comp.YesNoDimmer.update Comp.YesNoDimmer.activate model.deleteDimmer
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | deleteDimmer = dm }, Cmd.none, False )
 | 
			
		||||
 | 
			
		||||
        DeleteMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                ( dm, flag ) =
 | 
			
		||||
                    Comp.YesNoDimmer.update lm model.deleteDimmer
 | 
			
		||||
 | 
			
		||||
                cmd =
 | 
			
		||||
                    if flag then
 | 
			
		||||
                        Api.deleteCustomField flags model.field.id UpdateResp
 | 
			
		||||
 | 
			
		||||
                    else
 | 
			
		||||
                        Cmd.none
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | deleteDimmer = dm }, cmd, False )
 | 
			
		||||
 | 
			
		||||
        UpdateResp (Ok r) ->
 | 
			
		||||
            ( { model | result = Just r }, Cmd.none, r.success )
 | 
			
		||||
 | 
			
		||||
        UpdateResp (Err err) ->
 | 
			
		||||
            ( { model | result = Just (BasicResult False (Util.Http.errorToString err)) }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            , False
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Flags -> Model -> Html Msg
 | 
			
		||||
view _ model =
 | 
			
		||||
    let
 | 
			
		||||
        mkItem cft =
 | 
			
		||||
            Comp.FixedDropdown.Item cft (Data.CustomFieldType.label cft)
 | 
			
		||||
    in
 | 
			
		||||
    div [ class "ui error form segment" ]
 | 
			
		||||
        ([ Html.map DeleteMsg (Comp.YesNoDimmer.view model.deleteDimmer)
 | 
			
		||||
         , if model.field.id == "" then
 | 
			
		||||
            div []
 | 
			
		||||
                [ text "Create a new custom field."
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
           else
 | 
			
		||||
            div []
 | 
			
		||||
                [ text "Modify this custom field. Note that changing the type may result in data loss!"
 | 
			
		||||
                ]
 | 
			
		||||
         , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "ui message", True )
 | 
			
		||||
                , ( "invisible hidden", model.result == Nothing )
 | 
			
		||||
                , ( "error", Maybe.map .success model.result == Just False )
 | 
			
		||||
                , ( "success", Maybe.map .success model.result == Just True )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ Maybe.map .message model.result
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
         , div [ class "required field" ]
 | 
			
		||||
            [ label [] [ text "Name" ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetName
 | 
			
		||||
                , model.name
 | 
			
		||||
                    |> Maybe.withDefault ""
 | 
			
		||||
                    |> value
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div [ class "small-info" ]
 | 
			
		||||
                [ text "The name uniquely identifies this field. It must be a valid "
 | 
			
		||||
                , text "identifier, not contain spaces or weird characters."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
         , div [ class "field" ]
 | 
			
		||||
            [ label [] [ text "Label" ]
 | 
			
		||||
            , input
 | 
			
		||||
                [ type_ "text"
 | 
			
		||||
                , onInput SetLabel
 | 
			
		||||
                , model.label
 | 
			
		||||
                    |> Maybe.withDefault ""
 | 
			
		||||
                    |> value
 | 
			
		||||
                ]
 | 
			
		||||
                []
 | 
			
		||||
            , div [ class "small-info" ]
 | 
			
		||||
                [ 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 [ class "required field" ]
 | 
			
		||||
            [ label [] [ text "Field Type" ]
 | 
			
		||||
            , Html.map FTypeMsg
 | 
			
		||||
                (Comp.FixedDropdown.view
 | 
			
		||||
                    (Maybe.map mkItem model.ftype)
 | 
			
		||||
                    model.ftypeModel
 | 
			
		||||
                )
 | 
			
		||||
            , div [ class "small-info" ]
 | 
			
		||||
                [ text "A field must have a type. This defines how to input values and "
 | 
			
		||||
                , text "the server validates it according to this type."
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
         ]
 | 
			
		||||
            ++ viewButtons model
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewButtons : Model -> List (Html Msg)
 | 
			
		||||
viewButtons model =
 | 
			
		||||
    [ div [ class "ui divider" ] []
 | 
			
		||||
    , button
 | 
			
		||||
        [ class "ui primary button"
 | 
			
		||||
        , onClick SubmitForm
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Submit"
 | 
			
		||||
        ]
 | 
			
		||||
    , button
 | 
			
		||||
        [ class "ui button"
 | 
			
		||||
        , onClick GoBack
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Back"
 | 
			
		||||
        ]
 | 
			
		||||
    , button
 | 
			
		||||
        [ classList
 | 
			
		||||
            [ ( "ui red button", True )
 | 
			
		||||
            , ( "invisible hidden", model.field.id == "" )
 | 
			
		||||
            ]
 | 
			
		||||
        , onClick RequestDelete
 | 
			
		||||
        ]
 | 
			
		||||
        [ text "Delete"
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										190
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldManage.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldManage.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,190 @@
 | 
			
		||||
module Comp.CustomFieldManage exposing
 | 
			
		||||
    ( Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , empty
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.CustomFieldList exposing (CustomFieldList)
 | 
			
		||||
import Comp.CustomFieldDetail
 | 
			
		||||
import Comp.CustomFieldTable
 | 
			
		||||
import Data.Flags exposing (Flags)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick, onInput)
 | 
			
		||||
import Http
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
    { tableModel : Comp.CustomFieldTable.Model
 | 
			
		||||
    , detailModel : Maybe Comp.CustomFieldDetail.Model
 | 
			
		||||
    , fields : List CustomField
 | 
			
		||||
    , query : String
 | 
			
		||||
    , loading : Bool
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
    = TableMsg Comp.CustomFieldTable.Msg
 | 
			
		||||
    | DetailMsg Comp.CustomFieldDetail.Msg
 | 
			
		||||
    | CustomFieldListResp (Result Http.Error CustomFieldList)
 | 
			
		||||
    | SetQuery String
 | 
			
		||||
    | InitNewCustomField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
empty : Model
 | 
			
		||||
empty =
 | 
			
		||||
    { tableModel = Comp.CustomFieldTable.init
 | 
			
		||||
    , detailModel = Nothing
 | 
			
		||||
    , fields = []
 | 
			
		||||
    , query = ""
 | 
			
		||||
    , loading = False
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
init : Flags -> ( Model, Cmd Msg )
 | 
			
		||||
init flags =
 | 
			
		||||
    ( empty
 | 
			
		||||
    , Api.getCustomFields flags empty.query CustomFieldListResp
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Update
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
 | 
			
		||||
update flags msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
        TableMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                ( tm, action ) =
 | 
			
		||||
                    Comp.CustomFieldTable.update lm model.tableModel
 | 
			
		||||
 | 
			
		||||
                detail =
 | 
			
		||||
                    case action of
 | 
			
		||||
                        Comp.CustomFieldTable.EditAction item ->
 | 
			
		||||
                            Comp.CustomFieldDetail.init item |> Just
 | 
			
		||||
 | 
			
		||||
                        Comp.CustomFieldTable.NoAction ->
 | 
			
		||||
                            model.detailModel
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | tableModel = tm, detailModel = detail }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
        DetailMsg lm ->
 | 
			
		||||
            case model.detailModel of
 | 
			
		||||
                Just detail ->
 | 
			
		||||
                    let
 | 
			
		||||
                        ( dm, dc, back ) =
 | 
			
		||||
                            Comp.CustomFieldDetail.update flags lm detail
 | 
			
		||||
 | 
			
		||||
                        cmd =
 | 
			
		||||
                            if back then
 | 
			
		||||
                                Api.getCustomFields flags model.query CustomFieldListResp
 | 
			
		||||
 | 
			
		||||
                            else
 | 
			
		||||
                                Cmd.none
 | 
			
		||||
                    in
 | 
			
		||||
                    ( { model
 | 
			
		||||
                        | detailModel =
 | 
			
		||||
                            if back then
 | 
			
		||||
                                Nothing
 | 
			
		||||
 | 
			
		||||
                            else
 | 
			
		||||
                                Just dm
 | 
			
		||||
                      }
 | 
			
		||||
                    , Cmd.batch
 | 
			
		||||
                        [ Cmd.map DetailMsg dc
 | 
			
		||||
                        , cmd
 | 
			
		||||
                        ]
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                Nothing ->
 | 
			
		||||
                    ( model, Cmd.none )
 | 
			
		||||
 | 
			
		||||
        SetQuery str ->
 | 
			
		||||
            ( { model | query = str }
 | 
			
		||||
            , Api.getCustomFields flags str CustomFieldListResp
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        CustomFieldListResp (Ok sl) ->
 | 
			
		||||
            ( { model | fields = sl.items }, Cmd.none )
 | 
			
		||||
 | 
			
		||||
        CustomFieldListResp (Err _) ->
 | 
			
		||||
            ( model, Cmd.none )
 | 
			
		||||
 | 
			
		||||
        InitNewCustomField ->
 | 
			
		||||
            let
 | 
			
		||||
                sd =
 | 
			
		||||
                    Comp.CustomFieldDetail.initEmpty
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | detailModel = Just sd }
 | 
			
		||||
            , Cmd.none
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- View
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Flags -> Model -> Html Msg
 | 
			
		||||
view flags model =
 | 
			
		||||
    case model.detailModel of
 | 
			
		||||
        Just dm ->
 | 
			
		||||
            viewDetail flags dm
 | 
			
		||||
 | 
			
		||||
        Nothing ->
 | 
			
		||||
            viewTable model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewDetail : Flags -> Comp.CustomFieldDetail.Model -> Html Msg
 | 
			
		||||
viewDetail flags detailModel =
 | 
			
		||||
    div []
 | 
			
		||||
        [ Html.map DetailMsg (Comp.CustomFieldDetail.view flags detailModel)
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewTable : Model -> Html Msg
 | 
			
		||||
viewTable model =
 | 
			
		||||
    div []
 | 
			
		||||
        [ div [ class "ui secondary menu" ]
 | 
			
		||||
            [ div [ class "horizontally fitted item" ]
 | 
			
		||||
                [ div [ class "ui icon input" ]
 | 
			
		||||
                    [ input
 | 
			
		||||
                        [ type_ "text"
 | 
			
		||||
                        , onInput SetQuery
 | 
			
		||||
                        , value model.query
 | 
			
		||||
                        , placeholder "Search…"
 | 
			
		||||
                        ]
 | 
			
		||||
                        []
 | 
			
		||||
                    , i [ class "ui search icon" ]
 | 
			
		||||
                        []
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , div [ class "right menu" ]
 | 
			
		||||
                [ div [ class "item" ]
 | 
			
		||||
                    [ a
 | 
			
		||||
                        [ class "ui primary button"
 | 
			
		||||
                        , href "#"
 | 
			
		||||
                        , onClick InitNewCustomField
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ i [ class "plus icon" ] []
 | 
			
		||||
                        , text "New CustomField"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , Html.map TableMsg (Comp.CustomFieldTable.view model.tableModel model.fields)
 | 
			
		||||
        , div
 | 
			
		||||
            [ classList
 | 
			
		||||
                [ ( "ui dimmer", True )
 | 
			
		||||
                , ( "active", model.loading )
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
            [ div [ class "ui loader" ] []
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
							
								
								
									
										90
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldTable.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								modules/webapp/src/main/elm/Comp/CustomFieldTable.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
module Comp.CustomFieldTable exposing
 | 
			
		||||
    ( Action(..)
 | 
			
		||||
    , Model
 | 
			
		||||
    , Msg
 | 
			
		||||
    , init
 | 
			
		||||
    , update
 | 
			
		||||
    , view
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.CustomField exposing (CustomField)
 | 
			
		||||
import Api.Model.CustomFieldList exposing (CustomFieldList)
 | 
			
		||||
import Html exposing (..)
 | 
			
		||||
import Html.Attributes exposing (..)
 | 
			
		||||
import Html.Events exposing (onClick)
 | 
			
		||||
import Util.Html
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type alias Model =
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
    = EditItem CustomField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Action
 | 
			
		||||
    = NoAction
 | 
			
		||||
    | EditAction CustomField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
init : Model
 | 
			
		||||
init =
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
update : Msg -> Model -> ( Model, Action )
 | 
			
		||||
update msg model =
 | 
			
		||||
    case msg of
 | 
			
		||||
        EditItem item ->
 | 
			
		||||
            ( model, EditAction item )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
view : Model -> List CustomField -> Html Msg
 | 
			
		||||
view _ items =
 | 
			
		||||
    div []
 | 
			
		||||
        [ table [ class "ui very basic center aligned table" ]
 | 
			
		||||
            [ thead []
 | 
			
		||||
                [ tr []
 | 
			
		||||
                    [ th [ class "collapsing" ] []
 | 
			
		||||
                    , th [] [ text "Name/Label" ]
 | 
			
		||||
                    , th [] [ text "Type" ]
 | 
			
		||||
                    , th [] [ text "#Usage" ]
 | 
			
		||||
                    , th [] [ text "Created" ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            , tbody []
 | 
			
		||||
                (List.map viewItem items)
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewItem : CustomField -> Html Msg
 | 
			
		||||
viewItem item =
 | 
			
		||||
    tr []
 | 
			
		||||
        [ td [ class "collapsing" ]
 | 
			
		||||
            [ a
 | 
			
		||||
                [ href "#"
 | 
			
		||||
                , class "ui basic small blue label"
 | 
			
		||||
                , onClick (EditItem item)
 | 
			
		||||
                ]
 | 
			
		||||
                [ i [ class "edit icon" ] []
 | 
			
		||||
                , text "Edit"
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
        , td []
 | 
			
		||||
            [ text <| Maybe.withDefault item.name item.label
 | 
			
		||||
            ]
 | 
			
		||||
        , td []
 | 
			
		||||
            [ text item.ftype
 | 
			
		||||
            ]
 | 
			
		||||
        , td []
 | 
			
		||||
            [ String.fromInt item.usages
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        , td []
 | 
			
		||||
            [ Util.Time.formatDateShort item.created
 | 
			
		||||
                |> text
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
@@ -1449,6 +1449,9 @@ resetField flags item tagger field =
 | 
			
		||||
        Data.Fields.PreviewImage ->
 | 
			
		||||
            Cmd.none
 | 
			
		||||
 | 
			
		||||
        Data.Fields.CustomFields ->
 | 
			
		||||
            Cmd.none
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
resetHiddenFields :
 | 
			
		||||
    UiSettings
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										80
									
								
								modules/webapp/src/main/elm/Data/CustomFieldType.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								modules/webapp/src/main/elm/Data/CustomFieldType.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
module Data.CustomFieldType exposing
 | 
			
		||||
    ( CustomFieldType(..)
 | 
			
		||||
    , all
 | 
			
		||||
    , asString
 | 
			
		||||
    , fromString
 | 
			
		||||
    , label
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type CustomFieldType
 | 
			
		||||
    = Text
 | 
			
		||||
    | Numeric
 | 
			
		||||
    | Date
 | 
			
		||||
    | Boolean
 | 
			
		||||
    | Money
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all : List CustomFieldType
 | 
			
		||||
all =
 | 
			
		||||
    [ Text, Numeric, Date, Boolean, Money ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
asString : CustomFieldType -> String
 | 
			
		||||
asString ft =
 | 
			
		||||
    case ft of
 | 
			
		||||
        Text ->
 | 
			
		||||
            "text"
 | 
			
		||||
 | 
			
		||||
        Numeric ->
 | 
			
		||||
            "numeric"
 | 
			
		||||
 | 
			
		||||
        Date ->
 | 
			
		||||
            "date"
 | 
			
		||||
 | 
			
		||||
        Boolean ->
 | 
			
		||||
            "bool"
 | 
			
		||||
 | 
			
		||||
        Money ->
 | 
			
		||||
            "money"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
label : CustomFieldType -> String
 | 
			
		||||
label ft =
 | 
			
		||||
    case ft of
 | 
			
		||||
        Text ->
 | 
			
		||||
            "Text"
 | 
			
		||||
 | 
			
		||||
        Numeric ->
 | 
			
		||||
            "Numeric"
 | 
			
		||||
 | 
			
		||||
        Date ->
 | 
			
		||||
            "Date"
 | 
			
		||||
 | 
			
		||||
        Boolean ->
 | 
			
		||||
            "Boolean"
 | 
			
		||||
 | 
			
		||||
        Money ->
 | 
			
		||||
            "Money"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fromString : String -> Maybe CustomFieldType
 | 
			
		||||
fromString str =
 | 
			
		||||
    case String.toLower str of
 | 
			
		||||
        "text" ->
 | 
			
		||||
            Just Text
 | 
			
		||||
 | 
			
		||||
        "numeric" ->
 | 
			
		||||
            Just Numeric
 | 
			
		||||
 | 
			
		||||
        "date" ->
 | 
			
		||||
            Just Date
 | 
			
		||||
 | 
			
		||||
        "bool" ->
 | 
			
		||||
            Just Boolean
 | 
			
		||||
 | 
			
		||||
        "money" ->
 | 
			
		||||
            Just Money
 | 
			
		||||
 | 
			
		||||
        _ ->
 | 
			
		||||
            Nothing
 | 
			
		||||
@@ -20,6 +20,7 @@ type Field
 | 
			
		||||
    | DueDate
 | 
			
		||||
    | Direction
 | 
			
		||||
    | PreviewImage
 | 
			
		||||
    | CustomFields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all : List Field
 | 
			
		||||
@@ -35,6 +36,7 @@ all =
 | 
			
		||||
        , DueDate
 | 
			
		||||
        , Direction
 | 
			
		||||
        , PreviewImage
 | 
			
		||||
        , CustomFields
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -76,6 +78,9 @@ fromString str =
 | 
			
		||||
        "preview" ->
 | 
			
		||||
            Just PreviewImage
 | 
			
		||||
 | 
			
		||||
        "customfields" ->
 | 
			
		||||
            Just CustomFields
 | 
			
		||||
 | 
			
		||||
        _ ->
 | 
			
		||||
            Nothing
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +118,9 @@ toString field =
 | 
			
		||||
        PreviewImage ->
 | 
			
		||||
            "preview"
 | 
			
		||||
 | 
			
		||||
        CustomFields ->
 | 
			
		||||
            "customfields"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
label : Field -> String
 | 
			
		||||
label field =
 | 
			
		||||
@@ -147,6 +155,9 @@ label field =
 | 
			
		||||
        PreviewImage ->
 | 
			
		||||
            "Preview Image"
 | 
			
		||||
 | 
			
		||||
        CustomFields ->
 | 
			
		||||
            "Custom Fields"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fromList : List String -> List Field
 | 
			
		||||
fromList strings =
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ module Data.Icons exposing
 | 
			
		||||
    , concernedIcon
 | 
			
		||||
    , correspondent
 | 
			
		||||
    , correspondentIcon
 | 
			
		||||
    , customField
 | 
			
		||||
    , customFieldIcon
 | 
			
		||||
    , date
 | 
			
		||||
    , dateIcon
 | 
			
		||||
    , direction
 | 
			
		||||
@@ -33,6 +35,16 @@ import Html exposing (Html, i)
 | 
			
		||||
import Html.Attributes exposing (class)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
customField : String
 | 
			
		||||
customField =
 | 
			
		||||
    "highlighter icon"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
customFieldIcon : String -> Html msg
 | 
			
		||||
customFieldIcon classes =
 | 
			
		||||
    i [ class (customField ++ " " ++ classes) ] []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
search : String
 | 
			
		||||
search =
 | 
			
		||||
    "search icon"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ module Page.ManageData.Data exposing
 | 
			
		||||
    , init
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Comp.CustomFieldManage
 | 
			
		||||
import Comp.EquipmentManage
 | 
			
		||||
import Comp.FolderManage
 | 
			
		||||
import Comp.OrgManage
 | 
			
		||||
@@ -20,6 +21,7 @@ type alias Model =
 | 
			
		||||
    , orgManageModel : Comp.OrgManage.Model
 | 
			
		||||
    , personManageModel : Comp.PersonManage.Model
 | 
			
		||||
    , folderManageModel : Comp.FolderManage.Model
 | 
			
		||||
    , fieldManageModel : Comp.CustomFieldManage.Model
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -31,6 +33,7 @@ init _ =
 | 
			
		||||
      , orgManageModel = Comp.OrgManage.emptyModel
 | 
			
		||||
      , personManageModel = Comp.PersonManage.emptyModel
 | 
			
		||||
      , folderManageModel = Comp.FolderManage.empty
 | 
			
		||||
      , fieldManageModel = Comp.CustomFieldManage.empty
 | 
			
		||||
      }
 | 
			
		||||
    , Cmd.none
 | 
			
		||||
    )
 | 
			
		||||
@@ -42,6 +45,7 @@ type Tab
 | 
			
		||||
    | OrgTab
 | 
			
		||||
    | PersonTab
 | 
			
		||||
    | FolderTab
 | 
			
		||||
    | CustomFieldTab
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Msg
 | 
			
		||||
@@ -51,3 +55,4 @@ type Msg
 | 
			
		||||
    | OrgManageMsg Comp.OrgManage.Msg
 | 
			
		||||
    | PersonManageMsg Comp.PersonManage.Msg
 | 
			
		||||
    | FolderMsg Comp.FolderManage.Msg
 | 
			
		||||
    | CustomFieldMsg Comp.CustomFieldManage.Msg
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
module Page.ManageData.Update exposing (update)
 | 
			
		||||
 | 
			
		||||
import Comp.CustomFieldManage
 | 
			
		||||
import Comp.EquipmentManage
 | 
			
		||||
import Comp.FolderManage
 | 
			
		||||
import Comp.OrgManage
 | 
			
		||||
@@ -37,6 +38,13 @@ update flags msg model =
 | 
			
		||||
                    in
 | 
			
		||||
                    ( { m | folderManageModel = sm }, Cmd.map FolderMsg sc )
 | 
			
		||||
 | 
			
		||||
                CustomFieldTab ->
 | 
			
		||||
                    let
 | 
			
		||||
                        ( cm, cc ) =
 | 
			
		||||
                            Comp.CustomFieldManage.init flags
 | 
			
		||||
                    in
 | 
			
		||||
                    ( { m | fieldManageModel = cm }, Cmd.map CustomFieldMsg cc )
 | 
			
		||||
 | 
			
		||||
        TagManageMsg m ->
 | 
			
		||||
            let
 | 
			
		||||
                ( m2, c2 ) =
 | 
			
		||||
@@ -73,3 +81,12 @@ update flags msg model =
 | 
			
		||||
            ( { model | folderManageModel = m2 }
 | 
			
		||||
            , Cmd.map FolderMsg c2
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        CustomFieldMsg lm ->
 | 
			
		||||
            let
 | 
			
		||||
                ( m2, c2 ) =
 | 
			
		||||
                    Comp.CustomFieldManage.update flags lm model.fieldManageModel
 | 
			
		||||
            in
 | 
			
		||||
            ( { model | fieldManageModel = m2 }
 | 
			
		||||
            , Cmd.map CustomFieldMsg c2
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
module Page.ManageData.View exposing (view)
 | 
			
		||||
 | 
			
		||||
import Comp.CustomFieldManage
 | 
			
		||||
import Comp.EquipmentManage
 | 
			
		||||
import Comp.FolderManage
 | 
			
		||||
import Comp.OrgManage
 | 
			
		||||
@@ -65,6 +66,18 @@ view flags settings model =
 | 
			
		||||
                        [ Icons.folderIcon ""
 | 
			
		||||
                        , text "Folder"
 | 
			
		||||
                        ]
 | 
			
		||||
                    , div
 | 
			
		||||
                        [ classActive (model.currentTab == Just CustomFieldTab) "link icon item"
 | 
			
		||||
                        , classList
 | 
			
		||||
                            [ ( "invisible hidden"
 | 
			
		||||
                              , Data.UiSettings.fieldHidden settings Data.Fields.CustomFields
 | 
			
		||||
                              )
 | 
			
		||||
                            ]
 | 
			
		||||
                        , onClick (SetTab CustomFieldTab)
 | 
			
		||||
                        ]
 | 
			
		||||
                        [ Icons.customFieldIcon ""
 | 
			
		||||
                        , text "Custom Fields"
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]
 | 
			
		||||
                ]
 | 
			
		||||
            ]
 | 
			
		||||
@@ -86,6 +99,9 @@ view flags settings model =
 | 
			
		||||
                    Just FolderTab ->
 | 
			
		||||
                        viewFolder flags settings model
 | 
			
		||||
 | 
			
		||||
                    Just CustomFieldTab ->
 | 
			
		||||
                        viewCustomFields flags settings model
 | 
			
		||||
 | 
			
		||||
                    Nothing ->
 | 
			
		||||
                        []
 | 
			
		||||
                )
 | 
			
		||||
@@ -93,6 +109,18 @@ view flags settings model =
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewCustomFields : Flags -> UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewCustomFields flags _ model =
 | 
			
		||||
    [ h2 [ class "ui header" ]
 | 
			
		||||
        [ Icons.customFieldIcon ""
 | 
			
		||||
        , div [ class "content" ]
 | 
			
		||||
            [ text "Custom Fields"
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    , Html.map CustomFieldMsg (Comp.CustomFieldManage.view flags model.fieldManageModel)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
viewFolder : Flags -> UiSettings -> Model -> List (Html Msg)
 | 
			
		||||
viewFolder flags _ model =
 | 
			
		||||
    [ h2
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user