Skip to content

Commit c8d7d48

Browse files
committed
Add webhook configuration UI to project settings
The notification system supports webhooks. Add a way to configure then for each project on the project settings page. ! This does not yet add API integration
1 parent 8da3a98 commit c8d7d48

File tree

7 files changed

+495
-17
lines changed

7 files changed

+495
-17
lines changed

elm-git.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"git-dependencies": {
33
"direct": {
4-
"https://github.com/unisonweb/ui-core": "aedb50adb6e1ecbba981c1bea6c074cfdf1fffae"
4+
"https://github.com/unisonweb/ui-core": "caf8eaffac685566d964c1d3e4b63deea8e7462f"
55
},
66
"indirect": {}
77
}
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
module UnisonShare.AddProjectWebhookModal exposing (..)
2+
3+
import Html exposing (Html, div)
4+
import Html.Attributes exposing (class)
5+
import Http
6+
import Lib.HttpApi exposing (HttpResult)
7+
import List.Extra as ListE
8+
import List.Nonempty as NEL
9+
import UI
10+
import UI.Button as Button
11+
import UI.Divider as Divider
12+
import UI.Form.CheckboxField as CheckboxField
13+
import UI.Form.RadioField as RadioField
14+
import UI.Form.TextField as TextField
15+
import UI.Icon as Icon
16+
import UI.Modal as Modal
17+
import UI.ProfileSnippet as ProfileSnippet
18+
import UI.StatusBanner as StatusBanner
19+
import UnisonShare.AppContext exposing (AppContext)
20+
import UnisonShare.Link as Link
21+
import UnisonShare.Project.ProjectRef exposing (ProjectRef)
22+
import UnisonShare.ProjectWebhook exposing (ProjectWebhook)
23+
import UnisonShare.User exposing (UserSummaryWithId)
24+
25+
26+
type NotificationEventType
27+
= ProjectContributionCreated
28+
| ProjectContributionUpdated
29+
| ProjectContributionComment
30+
| ProjectTicketCreated
31+
| ProjectTicketUpdated
32+
| ProjectTicketComment
33+
| ProjectBranchUpdated
34+
| ProjectReleaseCreated
35+
36+
37+
type WebhookEvents
38+
= AllEvents
39+
| SpecificEvents (List NotificationEventType)
40+
41+
42+
type alias Form =
43+
{ url : String
44+
, events : WebhookEvents
45+
, isActive : Bool
46+
}
47+
48+
49+
type Model
50+
= Edit Form
51+
| Saving Form
52+
| Failure Http.Error Form
53+
| Success ProjectWebhook
54+
55+
56+
init : Model
57+
init =
58+
Edit { url = "", events = AllEvents, isActive = True }
59+
60+
61+
62+
-- UPDATE
63+
64+
65+
type Msg
66+
= CloseModal
67+
| UpdateUrl String
68+
| SetWebhookEvents WebhookEvents
69+
| ToggleEvent NotificationEventType
70+
| ToggleIsActive
71+
| AddWebhook
72+
| AddWebhookFinished (HttpResult ())
73+
74+
75+
type OutMsg
76+
= NoOutMsg
77+
| RequestCloseModal
78+
| AddedWebhook ProjectWebhook
79+
80+
81+
update : AppContext -> ProjectRef -> Msg -> Model -> ( Model, Cmd Msg, OutMsg )
82+
update _ _ msg model =
83+
case ( msg, model ) of
84+
( UpdateUrl url, Edit f ) ->
85+
( Edit { f | url = url }, Cmd.none, NoOutMsg )
86+
87+
( SetWebhookEvents events, Edit f ) ->
88+
( Edit { f | events = events }, Cmd.none, NoOutMsg )
89+
90+
( ToggleEvent eventType, Edit f ) ->
91+
let
92+
events =
93+
case f.events of
94+
AllEvents ->
95+
SpecificEvents [ eventType ]
96+
97+
SpecificEvents evts ->
98+
if List.member eventType evts then
99+
SpecificEvents (ListE.remove eventType evts)
100+
101+
else
102+
SpecificEvents (evts ++ [ eventType ])
103+
in
104+
( Edit { f | events = events }, Cmd.none, NoOutMsg )
105+
106+
( ToggleIsActive, Edit f ) ->
107+
( Edit { f | isActive = not f.isActive }, Cmd.none, NoOutMsg )
108+
109+
( AddWebhook, _ ) ->
110+
( model, Cmd.none, NoOutMsg )
111+
112+
( AddWebhookFinished _, _ ) ->
113+
( model, Cmd.none, NoOutMsg )
114+
115+
( CloseModal, _ ) ->
116+
( model, Cmd.none, RequestCloseModal )
117+
118+
_ ->
119+
( model, Cmd.none, NoOutMsg )
120+
121+
122+
123+
-- EFFECTS
124+
-- VIEW
125+
126+
127+
viewUser : UserSummaryWithId -> Html msg
128+
viewUser user =
129+
ProfileSnippet.profileSnippet user |> ProfileSnippet.view
130+
131+
132+
divider : Html msg
133+
divider =
134+
Divider.divider |> Divider.small |> Divider.view
135+
136+
137+
viewEventSelection : List NotificationEventType -> Html Msg
138+
viewEventSelection selected =
139+
let
140+
isSelected event =
141+
List.member event selected
142+
143+
checkbox title event =
144+
CheckboxField.field title (ToggleEvent event) (isSelected event)
145+
|> CheckboxField.view
146+
147+
contributionEvents =
148+
[ checkbox "Contribution created" ProjectContributionCreated
149+
, checkbox "Contribution updated" ProjectContributionUpdated
150+
, checkbox "Contribution comment" ProjectContributionComment
151+
]
152+
153+
ticketEvents =
154+
[ checkbox "Ticket created" ProjectTicketCreated
155+
, checkbox "Ticket updated" ProjectTicketUpdated
156+
, checkbox "Ticket comment" ProjectTicketComment
157+
]
158+
159+
eventSelectionCheckboxes =
160+
div [ class "event-selection_groups" ]
161+
[ div []
162+
[ div [ class "checkboxes" ]
163+
[ checkbox "Branch updated" ProjectBranchUpdated
164+
, checkbox "Release created" ProjectReleaseCreated
165+
]
166+
]
167+
, div []
168+
[ div [ class "checkboxes" ] contributionEvents
169+
]
170+
, div []
171+
[ div [ class "checkboxes" ] ticketEvents
172+
]
173+
]
174+
in
175+
div [ class "event-selection" ] [ divider, eventSelectionCheckboxes ]
176+
177+
178+
view : Model -> Html Msg
179+
view model =
180+
let
181+
modal_ c =
182+
Modal.content c
183+
|> Modal.modal "add-project-webhook-modal" CloseModal
184+
|> Modal.withHeader "Add Webhook"
185+
186+
modal =
187+
case model of
188+
Edit form ->
189+
let
190+
specificEventsOption =
191+
case form.events of
192+
AllEvents ->
193+
SpecificEvents []
194+
195+
_ ->
196+
form.events
197+
198+
options =
199+
NEL.singleton (RadioField.option "Select specific events" "The webhook is only called on selected events" specificEventsOption)
200+
|> NEL.cons (RadioField.option "All events" "The webhook is called on all project events (including future additions)" AllEvents)
201+
202+
eventSelection =
203+
case form.events of
204+
AllEvents ->
205+
UI.nothing
206+
207+
SpecificEvents selected ->
208+
viewEventSelection selected
209+
in
210+
modal_
211+
(div []
212+
[ TextField.field UpdateUrl "Webhook URL" form.url
213+
|> TextField.withIcon Icon.wireframeGlobe
214+
|> TextField.withHelpText "This URL will be called when the selected events are triggered."
215+
|> TextField.view
216+
, divider
217+
, RadioField.field "Events" SetWebhookEvents options form.events |> RadioField.view
218+
, eventSelection
219+
, divider
220+
, CheckboxField.field "Active" ToggleIsActive form.isActive
221+
|> CheckboxField.withHelpText "Actively call the Webhook URL when selected events are triggered."
222+
|> CheckboxField.view
223+
]
224+
)
225+
|> Modal.withActions
226+
[ Button.button CloseModal "Cancel"
227+
|> Button.subdued
228+
, Button.button AddWebhook "Add Webhook"
229+
|> Button.emphasized
230+
]
231+
|> Modal.withLeftSideFooter
232+
[ Button.iconThenLabel_ Link.docs Icon.docs "Webhook request format docs"
233+
|> Button.small
234+
|> Button.outlined
235+
|> Button.view
236+
]
237+
238+
Saving _ ->
239+
modal_ (StatusBanner.working "Adding Webhook...")
240+
241+
Failure _ _ ->
242+
modal_ (StatusBanner.bad "Failed to add Webhook")
243+
244+
Success _ ->
245+
modal_ (StatusBanner.good "Successfully added Webhook")
246+
in
247+
Modal.view modal

0 commit comments

Comments
 (0)