Skip to content

Commit dc4b3fa

Browse files
committed
Add NewOrgModal
Add a new app modal, that allows users to create organizations. It supports suggesting handles from the name and checking if handles are taken or not. It also allows the user to pick between public (free) orgs, and commercial (paid) orgs. When picking a commercial org, the backend will create a support ticket (in addition to the org).
1 parent 352208e commit dc4b3fa

File tree

14 files changed

+560
-23
lines changed

14 files changed

+560
-23
lines changed

elm-git.json

+1-1
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": "194adc73aceca4e3ae1519206e122419c9985f35"
4+
"https://github.com/unisonweb/ui-core": "78ba490fac82e43146be5653e5a9be2288ac6ad0"
55
},
66
"indirect": {}
77
}

elm.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"j-maas/elm-ordered-containers": "1.0.0",
3232
"justinmimbs/time-extra": "1.1.1",
3333
"krisajenkins/remotedata": "6.0.1",
34+
"kuon/elm-string-normalize": "1.0.6",
3435
"mgold/elm-nonempty-list": "4.2.0",
3536
"noahzgordon/elm-color-extra": "1.0.2",
3637
"rtfeldman/elm-iso8601-date-strings": "1.1.4",

src/UnisonShare/Account.elm

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type alias Account a =
1919
, pronouns : Maybe String
2020
, completedTours : List Tour
2121
, organizationMemberships : List OrganizationMembership
22+
, primaryEmail : String
2223
}
2324

2425

@@ -88,18 +89,20 @@ isProjectOwner projectRef account =
8889
decodeSummary : Decode.Decoder AccountSummary
8990
decodeSummary =
9091
let
91-
makeSummary handle name_ avatarUrl completedTours organizationMemberships =
92+
makeSummary handle name_ avatarUrl completedTours organizationMemberships primaryEmail =
9293
{ handle = handle
9394
, name = name_
9495
, avatarUrl = avatarUrl
9596
, pronouns = Nothing
9697
, completedTours = Maybe.withDefault [] completedTours
9798
, organizationMemberships = organizationMemberships
99+
, primaryEmail = primaryEmail
98100
}
99101
in
100-
Decode.map5 makeSummary
102+
Decode.map6 makeSummary
101103
(field "handle" UserHandle.decodeUnprefixed)
102104
(maybe (field "name" string))
103105
(maybe (field "avatarUrl" decodeUrl))
104106
(maybe (field "completedTours" (Decode.list Tour.decode)))
105107
(field "organizationMemberships" (Decode.list (Decode.map OrganizationMembership UserHandle.decodeUnprefixed)))
108+
(field "primaryEmail" string)

src/UnisonShare/Api.elm

+31-2
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,26 @@ import UnisonShare.Tour as Tour exposing (Tour)
3636
import Url.Builder exposing (QueryParameter, int, string)
3737

3838

39+
profile : UserHandle -> Endpoint
40+
profile handle =
41+
GET { path = [ "users", UserHandle.toUnprefixedString handle ], queryParams = [] }
42+
43+
3944
user : UserHandle -> Endpoint
4045
user handle =
4146
GET { path = [ "users", UserHandle.toUnprefixedString handle ], queryParams = [] }
4247

4348

49+
org : UserHandle -> Endpoint
50+
org handle =
51+
GET { path = [ "users", UserHandle.toUnprefixedString handle ], queryParams = [] }
52+
53+
4454
updateUserProfile : UserHandle -> { bio : String } -> Endpoint
45-
updateUserProfile handle profile =
55+
updateUserProfile handle profile_ =
4656
let
4757
body =
48-
Encode.object [ ( "bio", Encode.string profile.bio ) ]
58+
Encode.object [ ( "bio", Encode.string profile_.bio ) ]
4959
|> Http.jsonBody
5060
in
5161
PATCH
@@ -178,6 +188,25 @@ session =
178188
-- ORG ROLE ASSIGNMENTS (COLLABORATORS)
179189

180190

191+
createOrg : { a | handle : UserHandle, primaryEmail : String } -> String -> UserHandle -> Bool -> Endpoint
192+
createOrg owner name orgHandle isCommercial =
193+
let
194+
body =
195+
Encode.object
196+
[ ( "name", Encode.string name )
197+
, ( "handle", Encode.string (UserHandle.toUnprefixedString orgHandle) )
198+
, ( "isCommercial", Encode.bool isCommercial )
199+
, ( "owner", Encode.string (UserHandle.toUnprefixedString owner.handle) )
200+
, ( "email", Encode.string owner.primaryEmail )
201+
]
202+
in
203+
POST
204+
{ path = [ "orgs" ]
205+
, queryParams = []
206+
, body = Http.jsonBody body
207+
}
208+
209+
181210
orgRoleAssignments : UserHandle -> Endpoint
182211
orgRoleAssignments orgHandle =
183212
let

src/UnisonShare/App.elm

+34
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import UnisonShare.AppDocument as AppDocument
4040
import UnisonShare.AppError as AppError exposing (AppError)
4141
import UnisonShare.AppHeader as AppHeader
4242
import UnisonShare.Link as Link
43+
import UnisonShare.NewOrgModal as NewOrgModal
4344
import UnisonShare.Page.AcceptTermsPage as AcceptTermsPage
4445
import UnisonShare.Page.AccountPage as AccountPage
4546
import UnisonShare.Page.AppErrorPage as AppErrorPage
@@ -86,6 +87,7 @@ type Page
8687
type AppModal
8788
= NoModal
8889
| KeyboardShortcuts
90+
| NewOrg NewOrgModal.Model
8991

9092

9193
type alias Model =
@@ -213,6 +215,7 @@ type Msg
213215
| ToggleAccountMenu
214216
| ToggleCreateAccountMenu
215217
| ShowKeyboardShortcuts
218+
| ShowNewOrgModal
216219
| CloseModal
217220
| WhatsNewFetchFinished (HttpResult WhatsNew.LoadedWhatsNew)
218221
| WhatsNewMarkAllAsRead
@@ -224,6 +227,7 @@ type Msg
224227
| ProjectPageMsg ProjectPage.Msg
225228
| AccountPageMsg AccountPage.Msg
226229
| AcceptTermsPageMsg AcceptTermsPage.Msg
230+
| NewOrgModalMsg NewOrgModal.Msg
227231

228232

229233
update : Msg -> Model -> ( Model, Cmd Msg )
@@ -465,6 +469,9 @@ update msg ({ appContext } as model) =
465469
( _, ShowKeyboardShortcuts ) ->
466470
( { model | openedAppHeaderMenu = AppHeader.NoneOpened, appModal = KeyboardShortcuts }, Cmd.none )
467471

472+
( _, ShowNewOrgModal ) ->
473+
( { model | openedAppHeaderMenu = AppHeader.NoneOpened, appModal = NewOrg NewOrgModal.init }, Cmd.none )
474+
468475
( _, CloseModal ) ->
469476
( { model | appModal = NoModal }, Cmd.none )
470477

@@ -523,6 +530,29 @@ update msg ({ appContext } as model) =
523530
in
524531
( { model | page = AcceptTerms continueUrl acceptTerms_ }, Cmd.map AcceptTermsPageMsg acceptTermsCmd )
525532

533+
( _, NewOrgModalMsg newOrgMsg ) ->
534+
case ( model.appModal, appContext.session ) of
535+
( NewOrg newOrg, Session.SignedIn account ) ->
536+
let
537+
( newOrg_, cmd, out ) =
538+
NewOrgModal.update appContext account newOrgMsg newOrg
539+
540+
appModal =
541+
case out of
542+
NewOrgModal.NoOutMsg ->
543+
NewOrg newOrg_
544+
545+
NewOrgModal.RequestCloseModal ->
546+
NoModal
547+
548+
NewOrgModal.AddedOrg _ ->
549+
NoModal
550+
in
551+
( { model | appModal = appModal }, Cmd.map NewOrgModalMsg cmd )
552+
553+
_ ->
554+
( model, Cmd.none )
555+
526556
_ ->
527557
( model, Cmd.none )
528558

@@ -720,6 +750,7 @@ view model =
720750
, toggleAccountMenuMsg = ToggleAccountMenu
721751
, toggleCreateAccountMenuMsg = ToggleCreateAccountMenu
722752
, showKeyboardShortcutsModalMsg = ShowKeyboardShortcuts
753+
, showNewOrgModal = ShowNewOrgModal
723754
}
724755

725756
appDocument =
@@ -784,6 +815,9 @@ view model =
784815
KeyboardShortcuts ->
785816
{ appDocument | modal = Just (viewKeyboardShortcutsModal appContext.operatingSystem) }
786817

818+
NewOrg m ->
819+
{ appDocument | modal = Just (Html.map NewOrgModalMsg (NewOrgModal.view m)) }
820+
787821
appDocumentWithWelcomeTermsModal =
788822
-- We link to TermsOfService and PrivacyPolicy from the welcome
789823
-- terms modal and the AcceptTerms page is used during UCM signup,

src/UnisonShare/AppHeader.elm

+8-1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ type alias AppHeaderContext msg =
120120
, toggleHelpAndResourcesMenuMsg : msg
121121
, toggleAccountMenuMsg : msg
122122
, toggleCreateAccountMenuMsg : msg
123+
, showNewOrgModal : msg
123124
, showKeyboardShortcutsModalMsg : msg
124125
}
125126

@@ -306,6 +307,12 @@ view ctx appHeader_ =
306307
div [ classList [ ( "account-menu-trigger", True ), ( "account-menu_is-open", isOpen ) ] ]
307308
[ avatar, Icon.view chevron ]
308309

310+
newOrgButton =
311+
Button.iconThenLabel ctx.showNewOrgModal Icon.largePlus "New Org"
312+
|> Button.small
313+
|> Button.positive
314+
|> Button.view
315+
309316
accountMenu =
310317
ActionMenu.items
311318
(ActionMenu.optionItem Icon.cog "Account Settings" Link.account)
@@ -315,7 +322,7 @@ view ctx appHeader_ =
315322
|> ActionMenu.view
316323
|> (\a -> div [ class "account-menu" ] [ a ])
317324
in
318-
( nav, [ helpAndResources, accountMenu ] )
325+
( nav, [ newOrgButton, helpAndResources, accountMenu ] )
319326
in
320327
UI.AppHeader.appHeader (appTitle (Click.href "/"))
321328
|> UI.AppHeader.withNavigation navigation

0 commit comments

Comments
 (0)