add table views for all the various things

This commit is contained in:
Aaron Bieber 2024-04-18 08:03:07 -06:00
parent 06b62b5de5
commit 665694bd74
No known key found for this signature in database
11 changed files with 525 additions and 143 deletions

View File

@ -71,7 +71,7 @@
.form-container { .form-container {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(4, 1fr);
} }
.form-content { .form-content {
@ -81,6 +81,26 @@
color: red; color: red;
} }
table {
border: 1px solid #dedeff;
}
table tr td {
padding: 5px;
}
table th {
background-color: #dedeff;
}
table tr:nth-child(even) {
background-color: #f6f8f9;
}
table tr:nth-child(odd) {
background-color: #fff;
}
header { header {
padding: 0 !important; padding: 0 !important;
} }
@ -94,7 +114,6 @@
} }
footer { footer {
text-align: center;
} }
</style> </style>
</head> </head>

2
assets/main.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -175,6 +175,23 @@ func (q *Queries) AddWatchItem(ctx context.Context, arg AddWatchItemParams) (Wat
return i, err return i, err
} }
const deleteIgnore = `-- name: DeleteIgnore :exec
delete
from pull_request_ignores
where id = ?
and owner_id = ?
`
type DeleteIgnoreParams struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
}
func (q *Queries) DeleteIgnore(ctx context.Context, arg DeleteIgnoreParams) error {
_, err := q.db.ExecContext(ctx, deleteIgnore, arg.ID, arg.OwnerID)
return err
}
const deleteLink = `-- name: DeleteLink :exec const deleteLink = `-- name: DeleteLink :exec
delete delete
from links from links

View File

@ -6,6 +6,7 @@
"elm-version": "0.19.1", "elm-version": "0.19.1",
"dependencies": { "dependencies": {
"direct": { "direct": {
"billstclair/elm-sortable-table": "1.2.1",
"elm/browser": "1.0.2", "elm/browser": "1.0.2",
"elm/core": "1.0.5", "elm/core": "1.0.5",
"elm/html": "1.0.0", "elm/html": "1.0.0",

View File

@ -299,6 +299,50 @@ func linksPOST(w http.ResponseWriter, r *http.Request) {
} }
} }
func prignoreGET(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value(ownerKey).(int64)
if !ok {
http.Error(w, http.StatusText(http.StatusUnprocessableEntity), http.StatusUnprocessableEntity)
return
}
prIgnores, err := app.queries.GetAllPullRequestIgnores(app.ctx, ownerID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
prJson, err := json.Marshal(prIgnores)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-type", "application/json")
w.WriteHeader(200)
_, err = w.Write(prJson)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func prignoreDELETE(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ownerID, ok := ctx.Value(ownerKey).(int64)
if !ok {
http.Error(w, http.StatusText(http.StatusUnprocessableEntity), http.StatusUnprocessableEntity)
return
}
ignoreID, err := strconv.Atoi(chi.URLParam(r, "ignoreID"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
err = app.queries.DeleteIgnore(app.ctx, data.DeleteIgnoreParams{ID: int64(ignoreID), OwnerID: ownerID})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func prignorePOST(w http.ResponseWriter, r *http.Request) { func prignorePOST(w http.ResponseWriter, r *http.Request) {
d := &data.AddPullRequestIgnoreParams{} d := &data.AddPullRequestIgnoreParams{}
if err := render.Decode(r, d); err != nil { if err := render.Decode(r, d); err != nil {

View File

@ -156,6 +156,8 @@ func main() {
r.Route("/prignores", func(r chi.Router) { r.Route("/prignores", func(r chi.Router) {
r.Use(render.SetContentType(render.ContentTypeJSON)) r.Use(render.SetContentType(render.ContentTypeJSON))
r.Post("/", prignorePOST) r.Post("/", prignorePOST)
r.Get("/", prignoreGET)
r.Delete("/{ignoreID:[0-9]+}", prignoreDELETE)
}) })
r.Route("/icons", func(r chi.Router) { r.Route("/icons", func(r chi.Router) {
r.Use(IconCacher) r.Use(IconCacher)

View File

@ -82,6 +82,13 @@ from pull_requests
where id = ? where id = ?
and owner_id = ?; and owner_id = ?;
-- name: DeleteIgnore :exec
delete
from pull_request_ignores
where id = ?
and owner_id = ?;
-- name: GetAllPullRequestIgnores :many -- name: GetAllPullRequestIgnores :many
select * select *
from pull_request_ignores from pull_request_ignores

39
src/Ignores.elm Normal file
View File

@ -0,0 +1,39 @@
module Ignores exposing (..)
import Json.Decode
exposing
( Decoder
, field
, int
, list
, map5
, string
)
type alias Ignores =
List Ignore
type alias Ignore =
{ id : Int
, ownerId : Int
, createdAt : String
, repo : String
, number : Int
}
ignoreListDecoder : Decoder Ignores
ignoreListDecoder =
list ignoreDecoder
ignoreDecoder : Decoder Ignore
ignoreDecoder =
map5 Ignore
(field "id" int)
(field "owner_id" int)
(field "created_at" string)
(field "repo" string)
(field "number" int)

50
src/Links.elm Normal file
View File

@ -0,0 +1,50 @@
module Links exposing (..)
import Json.Decode
exposing
( Decoder
, bool
, field
, int
, list
, map6
, string
)
type alias Links =
List Link
type alias NewLink =
{ name : String
, url : String
, shared : Bool
, logo_url : String
}
type alias Link =
{ id : Int
, createdAt : String
, url : String
, name : String
, logoURL : String
, shared : Bool
}
linkListDecoder : Decoder Links
linkListDecoder =
list linkDecoder
linkDecoder : Decoder Link
linkDecoder =
map6 Link
(field "id" int)
(field "created_at" string)
(field "url" string)
(field "name" string)
(field "logo_url" string)
(field "shared" bool)

View File

@ -2,63 +2,25 @@ module Main exposing (..)
import Browser import Browser
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (checked, class, classList, href, name, placeholder, src, type_, value) import Html.Attributes
exposing
( checked
, class
, classList
, href
, name
, placeholder
, src
, type_
, value
)
import Html.Events exposing (..) import Html.Events exposing (..)
import Http import Http
import Json.Decode as Decode import Ignores
exposing
( Decoder
, bool
, field
, int
, list
, map5
, map6
, string
)
import Json.Encode as Encode import Json.Encode as Encode
import Links
import Table exposing (defaultCustomizations)
type alias Watches = import Watches
List Watch
type alias Links =
List Link
type alias Link =
{ id : Int
, createdAt : String
, url : String
, name : String
, logoURL : String
, shared : Bool
}
type alias Watch =
{ id : Int
, ownerId : Int
, name : String
, repo : String
, resultCount : Int
, results : List Node
}
type alias Node =
{ number : Int
, createdAt : String
, repository : RepoInfo
, title : String
, url : String
}
type alias RepoInfo =
{ nameWithOwner : String
}
main : Program () Model Msg main : Program () Model Msg
@ -78,49 +40,47 @@ type Msg
| DeleteLink Int | DeleteLink Int
| DeletedWatch (Result Http.Error ()) | DeletedWatch (Result Http.Error ())
| DeleteWatch Int | DeleteWatch Int
| GotLinks (Result Http.Error (List Link)) | DeletedIgnore (Result Http.Error ())
| GotNewLink NewLink | DeleteIgnore Int
| GotNewWatch NewWatch | GotLinks (Result Http.Error Links.Links)
| GotWatches (Result Http.Error (List Watch)) | GotNewLink Links.NewLink
| GotNewWatch Watches.NewWatch
| GotWatches (Result Http.Error Watches.Watches)
| GotIgnores (Result Http.Error Ignores.Ignores)
| HideWatchedItem Int String | HideWatchedItem Int String
| HidItem (Result Http.Error ()) | HidItem (Result Http.Error ())
| Reload | Reload
| ReloadLinks | ReloadLinks
| ReloadWatches | ReloadWatches
| ReloadIgnores
| SubmitLink | SubmitLink
| SubmitWatch | SubmitWatch
| FetchIcons | FetchIcons
| LoadIcons (Result Http.Error ()) | LoadIcons (Result Http.Error ())
| SetLinkTableState Table.State
| SetWatchTableState Table.State
| SetIgnoreTableState Table.State
type Status type Status
= Loading = Loading
| LoadedWatches (List Watch) | LoadedWatches Watches.Watches
| LoadedLinks (List Link) | LoadedIgnores Ignores.Ignores
| LoadedLinks Links.Links
| Errored String | Errored String
type alias NewWatch =
{ name : String
, repo : String
}
type alias NewLink =
{ name : String
, url : String
, shared : Bool
, logo_url : String
}
type alias Model = type alias Model =
{ watches : List Watch { watches : Watches.Watches
, links : List Link , links : Links.Links
, ignores : Ignores.Ignores
, errors : List String , errors : List String
, status : Status , status : Status
, newlink : NewLink , newlink : Links.NewLink
, newwatch : NewWatch , newwatch : Watches.NewWatch
, linkTableState : Table.State
, watchTableState : Table.State
, ignoreTableState : Table.State
} }
@ -128,6 +88,7 @@ initialModel : Model
initialModel = initialModel =
{ watches = [] { watches = []
, links = [] , links = []
, ignores = []
, errors = [] , errors = []
, status = Loading , status = Loading
, newlink = , newlink =
@ -140,12 +101,15 @@ initialModel =
{ name = "" { name = ""
, repo = "" , repo = ""
} }
, linkTableState = Table.initialSort "Created"
, watchTableState = Table.initialSort "Created"
, ignoreTableState = Table.initialSort "Created"
} }
init : () -> ( Model, Cmd Msg ) init : () -> ( Model, Cmd Msg )
init _ = init _ =
( initialModel, Cmd.batch [ getLinks, getWatches ] ) ( initialModel, Cmd.batch [ getLinks, getWatches, getIgnores ] )
hideWatched : Int -> String -> Cmd Msg hideWatched : Int -> String -> Cmd Msg
@ -222,6 +186,19 @@ deleteLink linkId =
} }
deleteIgnore : Int -> Cmd Msg
deleteIgnore ignoreId =
Http.request
{ url = "/prignores/" ++ String.fromInt ignoreId
, method = "DELETE"
, timeout = Nothing
, tracker = Nothing
, headers = []
, body = Http.emptyBody
, expect = Http.expectWhatever DeletedIgnore
}
deleteWatch : Int -> Cmd Msg deleteWatch : Int -> Cmd Msg
deleteWatch watchId = deleteWatch watchId =
Http.request Http.request
@ -253,6 +230,9 @@ update msg model =
DeleteWatch watchId -> DeleteWatch watchId ->
( model, deleteWatch watchId ) ( model, deleteWatch watchId )
DeleteIgnore ignoreId ->
( model, deleteIgnore ignoreId )
GotNewWatch newwatch -> GotNewWatch newwatch ->
( { model | newwatch = newwatch }, Cmd.none ) ( { model | newwatch = newwatch }, Cmd.none )
@ -283,6 +263,12 @@ update msg model =
DeletedWatch (Err _) -> DeletedWatch (Err _) ->
( { model | status = Errored "Server error deleting watch!" }, Cmd.none ) ( { model | status = Errored "Server error deleting watch!" }, Cmd.none )
DeletedIgnore (Ok _) ->
( model, getIgnores )
DeletedIgnore (Err _) ->
( { model | status = Errored "Server error deleting ignore!" }, Cmd.none )
HidItem (Err _) -> HidItem (Err _) ->
( { model | status = Errored "Server error when hiding a watch item!" }, Cmd.none ) ( { model | status = Errored "Server error when hiding a watch item!" }, Cmd.none )
@ -307,12 +293,18 @@ update msg model =
ReloadLinks -> ReloadLinks ->
( model, getLinks ) ( model, getLinks )
ReloadIgnores ->
( model, getIgnores )
GotWatches (Err _) -> GotWatches (Err _) ->
( { model | status = Errored "Server error when fetching watches!" }, Cmd.none ) ( { model | status = Errored "Server error when fetching watches!" }, Cmd.none )
GotLinks (Err _) -> GotLinks (Err _) ->
( { model | status = Errored "Server error when fetching links!" }, Cmd.none ) ( { model | status = Errored "Server error when fetching links!" }, Cmd.none )
GotIgnores (Err _) ->
( { model | status = Errored "Server error when fetching ignores!" }, Cmd.none )
GotWatches (Ok watches) -> GotWatches (Ok watches) ->
case watches of case watches of
_ :: _ -> _ :: _ ->
@ -340,6 +332,28 @@ update msg model =
[] -> [] ->
( { model | status = Errored "No Links found" }, Cmd.none ) ( { model | status = Errored "No Links found" }, Cmd.none )
GotIgnores (Ok ignores) ->
case ignores of
_ :: _ ->
( { model
| ignores = ignores
, status = LoadedIgnores ignores
}
, Cmd.none
)
[] ->
( { model | status = Errored "No Watches found" }, Cmd.none )
SetLinkTableState newState ->
( { model | linkTableState = newState }, Cmd.none )
SetWatchTableState newState ->
( { model | watchTableState = newState }, Cmd.none )
SetIgnoreTableState newState ->
( { model | ignoreTableState = newState }, Cmd.none )
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
subscriptions _ = subscriptions _ =
@ -381,18 +395,181 @@ view model =
, footer [] , footer []
[ details [] [ details []
[ summary [] [ summary []
[ b [] [ text "Maintenence" ] ] [ b [] [ text "Maintenence" ]
, button [ onClick FetchIcons ] [ text "Update Icons" ] ]
, div []
[ button [ onClick FetchIcons ] [ text "Update Icons" ]
]
, div []
[ h3 [] [ text "Links" ]
, Table.view linkTableConfig model.linkTableState model.links
]
, div []
[ h3 [] [ text "Watched Items" ]
, Table.view watchTableConfig model.watchTableState model.watches
]
, div []
[ h3
[]
[ text "Watched Items Ignores" ]
, Table.view ignoreTableConfig model.ignoreTableState model.ignores
]
] ]
] ]
] ]
shareTxt : Links.Link -> Table.HtmlDetails Msg
shareTxt link =
if link.shared then
Table.HtmlDetails []
[ text "Yes" ]
else
Table.HtmlDetails []
[ text "No" ]
shareColumn : Table.Column Links.Link Msg
shareColumn =
Table.veryCustomColumn
{ name = "Shared"
, viewData = \data -> shareTxt data
, sorter = Table.unsortable
}
linkTimeColumn : Table.Column Links.Link Msg
linkTimeColumn =
Table.customColumn
{ name = "Created"
, viewData = .createdAt
, sorter = Table.decreasingOrIncreasingBy .createdAt
}
deleteLinkColumn : Table.Column Links.Link Msg
deleteLinkColumn =
Table.veryCustomColumn
{ name = "Action"
, viewData = linkDeleteView
, sorter = Table.unsortable
}
linkTableConfig : Table.Config Links.Link Msg
linkTableConfig =
Table.customConfig
{ toId = .name
, toMsg = SetLinkTableState
, columns =
[ Table.stringColumn "Name" .name
, Table.stringColumn "URL" .url
, shareColumn
, Table.stringColumn "Logo URL" .logoURL
, linkTimeColumn
, deleteLinkColumn
]
, customizations = defaultCustomizations
}
linkDeleteView : Links.Link -> Table.HtmlDetails Msg
linkDeleteView { id } =
Table.HtmlDetails []
[ button
[ onClick (DeleteLink id) ]
[ text "Delete" ]
]
watchTableConfig : Table.Config Watches.Watch Msg
watchTableConfig =
Table.config
{ toId = .name
, toMsg = SetWatchTableState
, columns =
[ Table.stringColumn "Name" .name
, Table.stringColumn "Repo" .repo
, deleteWatchColumn
]
}
deleteWatchColumn : Table.Column Watches.Watch Msg
deleteWatchColumn =
Table.veryCustomColumn
{ name = "Action"
, viewData = watchDeleteView
, sorter = Table.unsortable
}
watchDeleteView : Watches.Watch -> Table.HtmlDetails Msg
watchDeleteView { id } =
Table.HtmlDetails []
[ button
[ onClick (DeleteWatch id) ]
[ text "Delete" ]
]
ignoreTimeColumn : Table.Column Ignores.Ignore Msg
ignoreTimeColumn =
Table.customColumn
{ name = "Created"
, viewData = .createdAt
, sorter = Table.decreasingOrIncreasingBy .createdAt
}
deleteIgnoreColumn : Table.Column Ignores.Ignore Msg
deleteIgnoreColumn =
Table.veryCustomColumn
{ name = "Action"
, viewData = ignoreDeleteView
, sorter = Table.unsortable
}
ignoreDeleteView : Ignores.Ignore -> Table.HtmlDetails Msg
ignoreDeleteView { id } =
Table.HtmlDetails []
[ button
[ onClick (DeleteIgnore id) ]
[ text "Delete" ]
]
ignoreTableConfig : Table.Config Ignores.Ignore Msg
ignoreTableConfig =
Table.customConfig
{ toId = .createdAt
, toMsg = SetIgnoreTableState
, columns =
[ Table.intColumn "ID" .id
, Table.stringColumn "Repo" .repo
, Table.intColumn "Number" .number
, ignoreTimeColumn
, deleteIgnoreColumn
]
, customizations = defaultCustomizations
}
getIgnores : Cmd Msg
getIgnores =
Http.get
{ url = "/prignores"
, expect = Http.expectJson GotIgnores Ignores.ignoreListDecoder
}
getLinks : Cmd Msg getLinks : Cmd Msg
getLinks = getLinks =
Http.get Http.get
{ url = "/links" { url = "/links"
, expect = Http.expectJson GotLinks linkListDecoder , expect = Http.expectJson GotLinks Links.linkListDecoder
} }
@ -400,11 +577,11 @@ getWatches : Cmd Msg
getWatches = getWatches =
Http.get Http.get
{ url = "/watches" { url = "/watches"
, expect = Http.expectJson GotWatches watchListDecoder , expect = Http.expectJson GotWatches Watches.watchListDecoder
} }
watchForm : Model -> NewWatch -> Html Msg watchForm : Model -> Watches.NewWatch -> Html Msg
watchForm model newwatch = watchForm model newwatch =
div [] div []
[ createForm "Watches" [ createForm "Watches"
@ -418,7 +595,7 @@ watchForm model newwatch =
] ]
linkForm : Model -> NewLink -> Html Msg linkForm : Model -> Links.NewLink -> Html Msg
linkForm model newlink = linkForm model newlink =
div [] div []
[ createForm "Links" [ createForm "Links"
@ -514,7 +691,7 @@ viewWatches model =
] ]
viewLink : Link -> Html Msg viewLink : Links.Link -> Html Msg
viewLink link = viewLink link =
div [] div []
[ div [ class "icon" ] [ div [ class "icon" ]
@ -532,7 +709,7 @@ viewLink link =
] ]
viewWatch : Watch -> Html Msg viewWatch : Watches.Watch -> Html Msg
viewWatch watch = viewWatch watch =
case watch.results of case watch.results of
[] -> [] ->
@ -555,7 +732,7 @@ viewWatch watch =
] ]
viewResult : Node -> Html Msg viewResult : Watches.Node -> Html Msg
viewResult node = viewResult node =
li [] li []
[ a [ href node.url ] [ text (String.fromInt node.number) ] [ a [ href node.url ] [ text (String.fromInt node.number) ]
@ -564,55 +741,3 @@ viewResult node =
, text " :: " , text " :: "
, text node.title , text node.title
] ]
-- DECODERS
linkListDecoder : Decoder (List Link)
linkListDecoder =
list linkDecoder
linkDecoder : Decoder Link
linkDecoder =
map6 Link
(field "id" int)
(field "created_at" string)
(field "url" string)
(field "name" string)
(field "logo_url" string)
(field "shared" bool)
watchListDecoder : Decoder (List Watch)
watchListDecoder =
list watchDecoder
watchDecoder : Decoder Watch
watchDecoder =
map6 Watch
(field "id" int)
(field "owner_id" int)
(field "name" string)
(field "repo" string)
(field "result_count" int)
(field "results" <| list resultsDecoder)
resultsDecoder : Decoder Node
resultsDecoder =
map5 Node
(field "number" int)
(field "createdAt" string)
(field "repository" repoInfoDecoder)
(field "title" string)
(field "url" string)
repoInfoDecoder : Decoder RepoInfo
repoInfoDecoder =
Decode.map RepoInfo
(field "nameWithOwner" string)

78
src/Watches.elm Normal file
View File

@ -0,0 +1,78 @@
module Watches exposing (..)
import Json.Decode as Decode
exposing
( Decoder
, field
, int
, list
, map5
, map6
, string
)
type alias NewWatch =
{ name : String
, repo : String
}
type alias Watches =
List Watch
type alias Watch =
{ id : Int
, ownerId : Int
, name : String
, repo : String
, resultCount : Int
, results : List Node
}
type alias RepoInfo =
{ nameWithOwner : String
}
type alias Node =
{ number : Int
, createdAt : String
, repository : RepoInfo
, title : String
, url : String
}
watchListDecoder : Decoder Watches
watchListDecoder =
list watchDecoder
watchDecoder : Decoder Watch
watchDecoder =
map6 Watch
(field "id" int)
(field "owner_id" int)
(field "name" string)
(field "repo" string)
(field "result_count" int)
(field "results" <| list resultsDecoder)
resultsDecoder : Decoder Node
resultsDecoder =
map5 Node
(field "number" int)
(field "createdAt" string)
(field "repository" repoInfoDecoder)
(field "title" string)
(field "url" string)
repoInfoDecoder : Decoder RepoInfo
repoInfoDecoder =
Decode.map RepoInfo
(field "nameWithOwner" string)