Set the folder of an item

This commit is contained in:
Eike Kettner 2020-07-11 12:00:19 +02:00
parent 5bde78083a
commit 86443e10a6
8 changed files with 155 additions and 6 deletions

View File

@ -24,6 +24,8 @@ trait OItem[F[_]] {
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult] def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult]
def setFolder(item: Ident, folder: Option[Ident], collective: Ident): F[AddResult]
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult]
def addCorrOrg(item: Ident, org: OOrganization.OrgAndContacts): F[AddResult] def addCorrOrg(item: Ident, org: OOrganization.OrgAndContacts): F[AddResult]
@ -131,6 +133,16 @@ object OItem {
.attempt .attempt
.map(AddResult.fromUpdate) .map(AddResult.fromUpdate)
def setFolder(
item: Ident,
folder: Option[Ident],
collective: Ident
): F[AddResult] =
store
.transact(RItem.updateFolder(item, collective, folder))
.attempt
.map(AddResult.fromUpdate)
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] = def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] =
store store
.transact(RItem.updateCorrOrg(item, collective, org)) .transact(RItem.updateCorrOrg(item, collective, org))

View File

@ -1365,6 +1365,31 @@ paths:
application/json: application/json:
schema: schema:
$ref: "#/components/schemas/BasicResult" $ref: "#/components/schemas/BasicResult"
/sec/item/{id}/folder:
put:
tags: [ Item ]
summary: Set a folder for this item.
description: |
Updates the folder property for this item to "place" the item
into a folder. If the request contains an empty object or an
`id` property of `null`, the item is moved into the "public"
or "root" folder.
security:
- authTokenHeader: []
parameters:
- $ref: "#/components/parameters/id"
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/OptionalId"
responses:
200:
description: Ok
content:
application/json:
schema:
$ref: "#/components/schemas/BasicResult"
/sec/item/{id}/corrOrg: /sec/item/{id}/corrOrg:
put: put:
tags: [ Item ] tags: [ Item ]
@ -3167,6 +3192,8 @@ components:
$ref: "#/components/schemas/IdName" $ref: "#/components/schemas/IdName"
inReplyTo: inReplyTo:
$ref: "#/components/schemas/IdName" $ref: "#/components/schemas/IdName"
folder:
$ref: "#/components/schemas/IdName"
dueDate: dueDate:
type: integer type: integer
format: date-time format: date-time

View File

@ -85,6 +85,7 @@ trait Conversions {
data.concPerson.map(p => IdName(p.pid, p.name)), data.concPerson.map(p => IdName(p.pid, p.name)),
data.concEquip.map(e => IdName(e.eid, e.name)), data.concEquip.map(e => IdName(e.eid, e.name)),
data.inReplyTo.map(mkIdName), data.inReplyTo.map(mkIdName),
data.folder.map(mkIdName),
data.item.dueDate, data.item.dueDate,
data.item.notes, data.item.notes,
data.attachments.map((mkAttachment(data) _).tupled).toList, data.attachments.map((mkAttachment(data) _).tupled).toList,

View File

@ -149,6 +149,13 @@ object ItemRoutes {
resp <- Ok(Conversions.basicResult(res, "Direction updated")) resp <- Ok(Conversions.basicResult(res, "Direction updated"))
} yield resp } yield resp
case req @ PUT -> Root / Ident(id) / "folder" =>
for {
idref <- req.as[OptionalId]
res <- backend.item.setFolder(id, idref.id, user.account.collective)
resp <- Ok(Conversions.basicResult(res, "Folder updated"))
} yield resp
case req @ PUT -> Root / Ident(id) / "corrOrg" => case req @ PUT -> Root / Ident(id) / "corrOrg" =>
for { for {
idref <- req.as[OptionalId] idref <- req.as[OptionalId]

View File

@ -66,6 +66,7 @@ object QItem {
concPerson: Option[RPerson], concPerson: Option[RPerson],
concEquip: Option[REquipment], concEquip: Option[REquipment],
inReplyTo: Option[IdRef], inReplyTo: Option[IdRef],
folder: Option[IdRef],
tags: Vector[RTag], tags: Vector[RTag],
attachments: Vector[(RAttachment, FileMeta)], attachments: Vector[(RAttachment, FileMeta)],
sources: Vector[(RAttachmentSource, FileMeta)], sources: Vector[(RAttachmentSource, FileMeta)],
@ -83,10 +84,11 @@ object QItem {
val P1C = RPerson.Columns.all.map(_.prefix("p1")) val P1C = RPerson.Columns.all.map(_.prefix("p1"))
val EC = REquipment.Columns.all.map(_.prefix("e")) val EC = REquipment.Columns.all.map(_.prefix("e"))
val ICC = List(RItem.Columns.id, RItem.Columns.name).map(_.prefix("ref")) val ICC = List(RItem.Columns.id, RItem.Columns.name).map(_.prefix("ref"))
val FC = List(RFolder.Columns.id, RFolder.Columns.name).map(_.prefix("f"))
val cq = val cq =
selectSimple( selectSimple(
IC ++ OC ++ P0C ++ P1C ++ EC ++ ICC, IC ++ OC ++ P0C ++ P1C ++ EC ++ ICC ++ FC,
RItem.table ++ fr"i", RItem.table ++ fr"i",
Fragment.empty Fragment.empty
) ++ ) ++
@ -105,6 +107,9 @@ object QItem {
fr"LEFT JOIN" ++ RItem.table ++ fr"ref ON" ++ RItem.Columns.inReplyTo fr"LEFT JOIN" ++ RItem.table ++ fr"ref ON" ++ RItem.Columns.inReplyTo
.prefix("i") .prefix("i")
.is(RItem.Columns.id.prefix("ref")) ++ .is(RItem.Columns.id.prefix("ref")) ++
fr"LEFT JOIN" ++ RFolder.table ++ fr"f ON" ++ RItem.Columns.folder
.prefix("i")
.is(RFolder.Columns.id.prefix("f")) ++
fr"WHERE" ++ RItem.Columns.id.prefix("i").is(id) fr"WHERE" ++ RItem.Columns.id.prefix("i").is(id)
val q = cq val q = cq
@ -115,6 +120,7 @@ object QItem {
Option[RPerson], Option[RPerson],
Option[RPerson], Option[RPerson],
Option[REquipment], Option[REquipment],
Option[IdRef],
Option[IdRef] Option[IdRef]
) )
] ]
@ -132,7 +138,7 @@ object QItem {
arch <- archives arch <- archives
ts <- tags ts <- tags
} yield data.map(d => } yield data.map(d =>
ItemData(d._1, d._2, d._3, d._4, d._5, d._6, ts, att, srcs, arch) ItemData(d._1, d._2, d._3, d._4, d._5, d._6, d._7, ts, att, srcs, arch)
) )
} }

View File

@ -82,7 +82,7 @@ object RItem {
val created = Column("created") val created = Column("created")
val updated = Column("updated") val updated = Column("updated")
val notes = Column("notes") val notes = Column("notes")
val folder = Column("folder_id") val folder = Column("folder_id")
val all = List( val all = List(
id, id,
cid, cid,
@ -243,7 +243,17 @@ object RItem {
n <- updateRow( n <- updateRow(
table, table,
and(cid.is(coll), concEquipment.is(Some(currentEquip))), and(cid.is(coll), concEquipment.is(Some(currentEquip))),
commas(concPerson.setTo(None: Option[Ident]), updated.setTo(t)) commas(concEquipment.setTo(None: Option[Ident]), updated.setTo(t))
).update.run
} yield n
def updateFolder(itemId: Ident, coll: Ident, folderId: Option[Ident]): ConnectionIO[Int] =
for {
t <- currentTime
n <- updateRow(
table,
and(cid.is(coll), id.is(itemId)),
commas(folder.setTo(folderId), updated.setTo(t))
).update.run ).update.run
} yield n } yield n

View File

@ -77,6 +77,7 @@ module Api exposing
, setCorrOrg , setCorrOrg
, setCorrPerson , setCorrPerson
, setDirection , setDirection
, setFolder
, setItemDate , setItemDate
, setItemDueDate , setItemDueDate
, setItemName , setItemName
@ -1262,6 +1263,16 @@ setDirection flags item dir receive =
} }
setFolder : Flags -> String -> OptionalId -> (Result Http.Error BasicResult -> msg) -> Cmd msg
setFolder flags item id receive =
Http2.authPut
{ url = flags.config.baseUrl ++ "/api/v1/sec/item/" ++ item ++ "/folder"
, account = getAccount flags
, body = Http.jsonBody (Api.Model.OptionalId.encode id)
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
}
setCorrOrg : Flags -> String -> OptionalId -> (Result Http.Error BasicResult -> msg) -> Cmd msg setCorrOrg : Flags -> String -> OptionalId -> (Result Http.Error BasicResult -> msg) -> Cmd msg
setCorrOrg flags item id receive = setCorrOrg flags item id receive =
Http2.authPut Http2.authPut

View File

@ -11,6 +11,8 @@ import Api.Model.Attachment exposing (Attachment)
import Api.Model.BasicResult exposing (BasicResult) import Api.Model.BasicResult exposing (BasicResult)
import Api.Model.DirectionValue exposing (DirectionValue) import Api.Model.DirectionValue exposing (DirectionValue)
import Api.Model.EquipmentList exposing (EquipmentList) import Api.Model.EquipmentList exposing (EquipmentList)
import Api.Model.FolderItem exposing (FolderItem)
import Api.Model.FolderList exposing (FolderList)
import Api.Model.IdName exposing (IdName) import Api.Model.IdName exposing (IdName)
import Api.Model.ItemDetail exposing (ItemDetail) import Api.Model.ItemDetail exposing (ItemDetail)
import Api.Model.ItemProposals exposing (ItemProposals) import Api.Model.ItemProposals exposing (ItemProposals)
@ -71,6 +73,7 @@ type alias Model =
, corrPersonModel : Comp.Dropdown.Model IdName , corrPersonModel : Comp.Dropdown.Model IdName
, concPersonModel : Comp.Dropdown.Model IdName , concPersonModel : Comp.Dropdown.Model IdName
, concEquipModel : Comp.Dropdown.Model IdName , concEquipModel : Comp.Dropdown.Model IdName
, folderModel : Comp.Dropdown.Model IdName
, nameModel : String , nameModel : String
, notesModel : Maybe String , notesModel : Maybe String
, notesField : NotesField , notesField : NotesField
@ -165,6 +168,11 @@ emptyModel =
{ makeOption = \e -> { value = e.id, text = e.name } { makeOption = \e -> { value = e.id, text = e.name }
, placeholder = "" , placeholder = ""
} }
, folderModel =
Comp.Dropdown.makeSingle
{ makeOption = \e -> { value = e.id, text = e.name }
, placeholder = ""
}
, nameModel = "" , nameModel = ""
, notesModel = Nothing , notesModel = Nothing
, notesField = ViewNotes , notesField = ViewNotes
@ -268,6 +276,8 @@ type Msg
| EditAttachNameSet String | EditAttachNameSet String
| EditAttachNameSubmit | EditAttachNameSubmit
| EditAttachNameResp (Result Http.Error BasicResult) | EditAttachNameResp (Result Http.Error BasicResult)
| GetFolderResp (Result Http.Error FolderList)
| FolderDropdownMsg (Comp.Dropdown.Msg IdName)
@ -281,6 +291,7 @@ getOptions flags =
, Api.getOrgLight flags GetOrgResp , Api.getOrgLight flags GetOrgResp
, Api.getPersonsLight flags GetPersonResp , Api.getPersonsLight flags GetPersonResp
, Api.getEquipments flags "" GetEquipResp , Api.getEquipments flags "" GetEquipResp
, Api.getFolders flags "" False GetFolderResp
] ]
@ -310,6 +321,16 @@ setDirection flags model =
Cmd.none Cmd.none
setFolder : Flags -> Model -> Maybe IdName -> Cmd Msg
setFolder flags model mref =
let
idref =
Maybe.map .id mref
|> OptionalId
in
Api.setFolder flags model.item.id idref SaveResp
setCorrOrg : Flags -> Model -> Maybe IdName -> Cmd Msg setCorrOrg : Flags -> Model -> Maybe IdName -> Cmd Msg
setCorrOrg flags model mref = setCorrOrg flags model mref =
let let
@ -523,6 +544,20 @@ update key flags next msg model =
( m7, c7, s7 ) = ( m7, c7, s7 ) =
update key flags next AddFilesReset m6 update key flags next AddFilesReset m6
( m8, c8, s8 ) =
update key
flags
next
(FolderDropdownMsg
(Comp.Dropdown.SetSelection
(item.folder
|> Maybe.map List.singleton
|> Maybe.withDefault []
)
)
)
m7
proposalCmd = proposalCmd =
if item.state == "created" then if item.state == "created" then
Api.getItemProposals flags item.id GetProposalResp Api.getItemProposals flags item.id GetProposalResp
@ -530,7 +565,7 @@ update key flags next msg model =
else else
Cmd.none Cmd.none
in in
( { m7 ( { m8
| item = item | item = item
, nameModel = item.name , nameModel = item.name
, notesModel = item.notes , notesModel = item.notes
@ -548,11 +583,12 @@ update key flags next msg model =
, c5 , c5
, c6 , c6
, c7 , c7
, c8
, getOptions flags , getOptions flags
, proposalCmd , proposalCmd
, Api.getSentMails flags item.id SentMailsResp , Api.getSentMails flags item.id SentMailsResp
] ]
, Sub.batch [ s1, s2, s3, s4, s5, s6, s7 ] , Sub.batch [ s1, s2, s3, s4, s5, s6, s7, s8 ]
) )
SetActiveAttachment pos -> SetActiveAttachment pos ->
@ -575,6 +611,26 @@ update key flags next msg model =
else else
noSub ( model, Api.itemDetail flags model.item.id GetItemResp ) noSub ( model, Api.itemDetail flags model.item.id GetItemResp )
FolderDropdownMsg m ->
let
( m2, c2 ) =
Comp.Dropdown.update m model.folderModel
newModel =
{ model | folderModel = m2 }
idref =
Comp.Dropdown.getSelected m2 |> List.head
save =
if isDropdownChangeMsg m then
setFolder flags newModel idref
else
Cmd.none
in
noSub ( newModel, Cmd.batch [ save, Cmd.map FolderDropdownMsg c2 ] )
TagDropdownMsg m -> TagDropdownMsg m ->
let let
( m2, c2 ) = ( m2, c2 ) =
@ -827,6 +883,18 @@ update key flags next msg model =
SetDueDateSuggestion date -> SetDueDateSuggestion date ->
noSub ( model, setDueDate flags model (Just date) ) noSub ( model, setDueDate flags model (Just date) )
GetFolderResp (Ok fs) ->
let
opts =
fs.items
|> List.map (\e -> IdName e.id e.name)
|> Comp.Dropdown.SetOptions
in
update key flags next (FolderDropdownMsg opts) model
GetFolderResp (Err _) ->
noSub ( model, Cmd.none )
GetTagsResp (Ok tags) -> GetTagsResp (Ok tags) ->
let let
tagList = tagList =
@ -2082,6 +2150,13 @@ renderEditForm settings model =
] ]
] ]
] ]
, div [ class "field" ]
[ label []
[ Icons.folderIcon "grey"
, text "Folder"
]
, Html.map FolderDropdownMsg (Comp.Dropdown.view settings model.folderModel)
]
, div [ class "field" ] , div [ class "field" ]
[ label [] [ label []
[ Icons.directionIcon "grey" [ Icons.directionIcon "grey"