summaryrefslogtreecommitdiff
path: root/elm3/ProdEdit
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2019-07-25 14:30:04 +0200
committerYorhel <git@yorhel.nl>2019-07-25 14:36:21 +0200
commitf296495a912ce759df11c43e78b4552788bdbff2 (patch)
tree0c10802de65fb7c8475722e12234bff5eb980628 /elm3/ProdEdit
parent0f3cfeb85caec6424bcbea47142eefbf8011636b (diff)
Merge the v3 branch into separate namespace + fix Docker stuff (again)
I was getting tired of having to keep two branches up-to-date with the latest developments, so decided to throw v3 into the same branch - just different files (...which will get mostly rewritten again soon). The two versions aren't very different in terms of dependencies, build system and support code, so they can now properly share files. Added a section to the README to avoid confusion. This merge also makes it easier to quickly switch between the different versions, which is handy for development. It's even possible to run both at the same time, but my scripts use the same port so that needs a workaround. And it's amazing how often I break the Docker scripts.
Diffstat (limited to 'elm3/ProdEdit')
-rw-r--r--elm3/ProdEdit/General.elm78
-rw-r--r--elm3/ProdEdit/Main.elm158
-rw-r--r--elm3/ProdEdit/Names.elm81
-rw-r--r--elm3/ProdEdit/New.elm12
-rw-r--r--elm3/ProdEdit/Relations.elm80
5 files changed, 409 insertions, 0 deletions
diff --git a/elm3/ProdEdit/General.elm b/elm3/ProdEdit/General.elm
new file mode 100644
index 00000000..19ca79b7
--- /dev/null
+++ b/elm3/ProdEdit/General.elm
@@ -0,0 +1,78 @@
+module ProdEdit.General exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Lib.Html exposing (..)
+import Lib.Gen exposing (languages, weburlPattern, producerTypes, ProdEdit)
+import Lib.Util exposing (..)
+
+
+type alias Model =
+ { desc : String
+ , l_wp : String
+ , lang : String
+ , ptype : String
+ , website : String
+ }
+
+
+init : ProdEdit -> Model
+init d =
+ { desc = d.desc
+ , l_wp = d.l_wp
+ , lang = d.lang
+ , ptype = d.ptype
+ , website = d.website
+ }
+
+
+new : Model
+new =
+ { desc = ""
+ , l_wp = ""
+ , lang = "ja"
+ , ptype = "co"
+ , website = ""
+ }
+
+
+type Msg
+ = Desc String
+ | LWP String
+ | Lang String
+ | PType String
+ | Website String
+
+
+update : Msg -> Model -> Model
+update msg model =
+ case msg of
+ Desc s -> { model | desc = s }
+ LWP s -> { model | l_wp = s }
+ Lang s -> { model | lang = s }
+ PType s -> { model | ptype = s }
+ Website s -> { model | website = s }
+
+
+view : Model -> (Msg -> a) -> List (Html a) -> Html a
+view model wrap names = card "general" "General info" [] <|
+ names ++ List.map (Html.map wrap)
+ [ cardRow "Meta" Nothing <| formGroups
+ [ [ label [for "ptype"] [ text "Type" ]
+ , inputSelect [id "ptype", name "ptype", onInput PType] model.ptype producerTypes
+ ]
+ , [ label [for "lang"] [ text "Primary language" ]
+ , inputSelect [id "lang", name "lang", onInput Lang] model.lang languages
+ ]
+ , [ label [for "website"] [ text "Official Website" ]
+ , inputText "website" model.website Website [pattern weburlPattern]
+ ]
+ , [ label [] [ text "Wikipedia" ]
+ , p [] [ text "https://en.wikipedia.org/wiki/", inputText "l_wp" model.l_wp LWP [class "form-control--inline", maxlength 100] ]
+ ]
+ ]
+
+ , cardRow "Description" (Just "English please!") <| formGroup
+ [ inputTextArea "desc" model.desc Desc [rows 8] ]
+ ]
diff --git a/elm3/ProdEdit/Main.elm b/elm3/ProdEdit/Main.elm
new file mode 100644
index 00000000..6d77ba7a
--- /dev/null
+++ b/elm3/ProdEdit/Main.elm
@@ -0,0 +1,158 @@
+module ProdEdit.Main exposing (Model, Msg, main, new, view, update)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Json.Encode as JE
+import Browser
+import Browser.Navigation exposing (load)
+import Lib.Util exposing (splitLn)
+import Lib.Html exposing (..)
+import Lib.Gen exposing (..)
+import Lib.Api as Api
+import Lib.Editsum as Editsum
+import ProdEdit.Names as Names
+import ProdEdit.General as Gen
+import ProdEdit.Relations as Rel
+
+
+main : Program ProdEdit Model Msg
+main = Browser.element
+ { init = \e -> (init e, Cmd.none)
+ , view = view
+ , update = update
+ , subscriptions = always Sub.none
+ }
+
+
+type alias Model =
+ { state : Api.State
+ , new : Bool
+ , editsum : Editsum.Model
+ , names : Names.Model
+ , general : Gen.Model
+ , relations : Rel.Model
+ , id : Maybe Int
+ , dupProds : List Api.Producer
+ }
+
+
+init : ProdEdit -> Model
+init d =
+ { state = Api.Normal
+ , new = False
+ , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
+ , names = Names.init d
+ , general = Gen.init d
+ , relations = Rel.init d.relations
+ , id = d.id
+ , dupProds = []
+ }
+
+
+new : Model
+new =
+ { state = Api.Normal
+ , new = True
+ , editsum = Editsum.new
+ , names = Names.new
+ , general = Gen.new
+ , relations = Rel.init []
+ , id = Nothing
+ , dupProds = []
+ }
+
+
+encode : Model -> ProdEditSend
+encode model =
+ { editsum = model.editsum.editsum
+ , hidden = model.editsum.hidden
+ , locked = model.editsum.locked
+ , name = model.names.name
+ , original = model.names.original
+ , alias = model.names.alias
+ , desc = model.general.desc
+ , lang = model.general.lang
+ , ptype = model.general.ptype
+ , l_wp = model.general.l_wp
+ , website = model.general.website
+ , relations = List.map (\e -> { pid = e.pid, relation = e.relation }) model.relations.relations
+ }
+
+
+type Msg
+ = Editsum Editsum.Msg
+ | Submit
+ | Submitted Api.Response
+ | Names Names.Msg
+ | General Gen.Msg
+ | Relations Rel.Msg
+ | CheckDup
+ | RecvDup Api.Response
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ case msg of
+ Names m -> ({ model | names = Names.update m model.names, dupProds = [] }, Cmd.none)
+ General m -> ({ model | general = Gen.update m model.general }, Cmd.none)
+ Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
+ Relations m -> let (nm, c) = Rel.update m model.relations in ({ model | relations = nm }, Cmd.map Relations c)
+
+ Submit ->
+ let
+ path =
+ case model.id of
+ Just id -> "/p" ++ String.fromInt id ++ "/edit"
+ Nothing -> "/p/add"
+ body = prodeditSendEncode (encode model)
+ in ({ model | state = Api.Loading }, Api.post path body Submitted)
+
+ Submitted (Api.Changed id rev) -> (model, load <| "/p" ++ String.fromInt id ++ "." ++ String.fromInt rev)
+ Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
+
+ CheckDup ->
+ let body = JE.object
+ [ ("search", JE.list JE.string <| List.filter ((/=)"") <| model.names.name :: model.names.original :: model.names.aliasList)
+ , ("hidden", JE.bool True) ]
+ in
+ if List.isEmpty model.dupProds
+ then ({ model | state = Api.Loading }, Api.post "/js/producer.json" body RecvDup)
+ else ({ model | new = False }, Cmd.none)
+
+ RecvDup (Api.ProducerResult dup) ->
+ ({ model | state = Api.Normal, dupProds = dup, new = not (List.isEmpty dup) }, Cmd.none)
+ RecvDup r -> ({ model | state = Api.Error r }, Cmd.none)
+
+
+
+isValid : Model -> Bool
+isValid model = not
+ ( model.names.aliasDuplicates
+ || model.relations.duplicates
+ )
+
+
+view : Model -> Html Msg
+view model =
+ if model.new
+ then form_ CheckDup (model.state == Api.Loading)
+ [ card "new" "Add a new producer" []
+ <| List.map (Html.map Names) <| Names.view model.names
+ , if List.isEmpty model.dupProds
+ then text ""
+ else card "dup" "Possible duplicates" [ div [ class "card__subheading" ] [ text "Please check the list below for possible duplicates." ] ]
+ [ cardRow "" Nothing <| formGroup [ div [ class "form-group__help" ] [
+ ul [] <| List.map (\e ->
+ li [] [ a [ href <| "/p" ++ String.fromInt e.id, title e.original, target "_black" ] [ text e.name ]
+ , text <| if e.hidden then " (deleted)" else "" ]
+ ) model.dupProds
+ ] ] ]
+ , submitButton "Continue" model.state (isValid model) False
+ ]
+
+ else form_ Submit (model.state == Api.Loading)
+ [ Gen.view model.general General <| List.map (Html.map Names) <| Names.view model.names
+ , Html.map Relations <| Rel.view model.relations
+ , Html.map Editsum <| Editsum.view model.editsum
+ , submitButton "Submit" model.state (isValid model) False
+ ]
diff --git a/elm3/ProdEdit/Names.elm b/elm3/ProdEdit/Names.elm
new file mode 100644
index 00000000..e28f4916
--- /dev/null
+++ b/elm3/ProdEdit/Names.elm
@@ -0,0 +1,81 @@
+module ProdEdit.Names exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Dict
+import Lib.Html exposing (..)
+import Lib.Gen exposing (ProdEdit)
+import Lib.Util exposing (..)
+
+
+type alias Model =
+ { name : String
+ , original : String
+ , alias : String
+ , aliasList : List String
+ , aliasDuplicates : Bool
+ }
+
+
+init : ProdEdit -> Model
+init d =
+ { name = d.name
+ , original = d.original
+ , alias = d.alias
+ , aliasList = splitLn d.alias
+ , aliasDuplicates = False
+ }
+
+
+new : Model
+new =
+ { name = ""
+ , original = ""
+ , alias = ""
+ , aliasList = []
+ , aliasDuplicates = False
+ }
+
+
+type Msg
+ = Name String
+ | Original String
+ | Alias String
+
+
+update : Msg -> Model -> Model
+update msg model =
+ case msg of
+ Name s -> { model | name = s }
+ Original s -> { model | original = s }
+ Alias s ->
+ { model
+ | alias = s
+ , aliasList = splitLn s
+ , aliasDuplicates = hasDuplicates (model.name :: model.original :: splitLn s)
+ }
+
+
+view : Model -> List (Html Msg)
+view model =
+ [ cardRow "Name" Nothing <| formGroups
+ [ [ label [for "name"] [text "Name (romaji)"]
+ , inputText "name" model.name Name [required True, maxlength 200]
+ ]
+ , [ label [for "original"] [text "Original"]
+ , inputText "original" model.original Original [maxlength 200]
+ , div [class "form-group__help"] [text "The original name of this producer, leave blank if it already is in the Latin alphabet."]
+ ]
+ ]
+ , cardRow "Aliases" Nothing <| formGroup
+ [ inputTextArea "aliases" model.alias Alias
+ [ rows 4, maxlength 500
+ , classList [("is-invalid", model.aliasDuplicates)]
+ ]
+ , if model.aliasDuplicates
+ then div [class "invalid-feedback"]
+ [ text "There are duplicate aliases." ]
+ else text ""
+ , div [class "form-group__help"] [ text "(Un)official aliases, separated by a newline." ]
+ ]
+ ]
diff --git a/elm3/ProdEdit/New.elm b/elm3/ProdEdit/New.elm
new file mode 100644
index 00000000..c1df2ac4
--- /dev/null
+++ b/elm3/ProdEdit/New.elm
@@ -0,0 +1,12 @@
+module ProdEdit.New exposing (main)
+
+import Browser
+import ProdEdit.Main as Main
+
+main : Program () Main.Model Main.Msg
+main = Browser.element
+ { init = always (Main.new, Cmd.none)
+ , view = Main.view
+ , update = Main.update
+ , subscriptions = always Sub.none
+ }
diff --git a/elm3/ProdEdit/Relations.elm b/elm3/ProdEdit/Relations.elm
new file mode 100644
index 00000000..a74339f1
--- /dev/null
+++ b/elm3/ProdEdit/Relations.elm
@@ -0,0 +1,80 @@
+module ProdEdit.Relations exposing (Model, Msg, init, update, view)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Lib.Html exposing (..)
+import Lib.Gen exposing (ProdEditRelations, producerRelations)
+import Lib.Api exposing (Producer)
+import Lib.Util exposing (..)
+import Lib.Autocomplete as A
+
+
+type alias Model =
+ { relations : List ProdEditRelations
+ , search : A.Model Producer
+ , duplicates : Bool
+ }
+
+
+init : List ProdEditRelations -> Model
+init l =
+ { relations = l
+ , search = A.init
+ , duplicates = False
+ }
+
+
+type Msg
+ = Del Int
+ | Rel Int String
+ | Search (A.Msg Producer)
+
+
+searchConfig : A.Config Msg Producer
+searchConfig = { wrap = Search, id = "add-relation", source = A.producerSource }
+
+
+validate : Model -> Model
+validate model = { model | duplicates = hasDuplicates <| List.map .pid model.relations }
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ case msg of
+ Del i -> (validate { model | relations = delidx i model.relations }, Cmd.none)
+ Rel i s -> (validate { model | relations = modidx i (\e -> { e | relation = s }) model.relations }, Cmd.none)
+ Search m ->
+ let (nm, c, res) = A.update searchConfig m model.search
+ in case res of
+ Nothing -> ({ model | search = nm }, c)
+ Just r ->
+ let
+ rel = List.head producerRelations |> Maybe.map Tuple.first |> Maybe.withDefault ""
+ nrow = { pid = r.id, relation = rel, name = r.name }
+ in (validate { model | search = A.clear nm, relations = model.relations ++ [nrow] }, c)
+
+
+view : Model -> Html Msg
+view model =
+ let
+ entry n e = editListRow "row--ai-center"
+ [ editListField 1 "single-line"
+ [ a [href <| "/p" ++ String.fromInt e.pid, title e.name, target "_blank" ] [text e.name ] ]
+ , editListField 1 ""
+ [ inputSelect [onInput (Rel n)] e.relation producerRelations ]
+ , editListField 0 "" [ removeButton (Del n) ]
+ ]
+
+ in card "relations" "Relations" [] <|
+ editList (List.indexedMap entry model.relations)
+ ++ formGroups (
+ (if model.duplicates
+ then [ [ div [ class "invalid-feedback" ]
+ [ text "The list contains duplicates. Make sure that the same producer is not listed multiple times." ] ] ]
+ else []
+ ) ++
+ [ label [for "add-relation"] [text "Add relation"]
+ :: A.view searchConfig model.search [placeholder "Producer...", style "max-width" "400px"]
+ ]
+ )