diff --git a/melon-head/src/App.res b/melon-head/src/App.res index 87d1ebd..ea24ddc 100644 --- a/melon-head/src/App.res +++ b/melon-head/src/App.res @@ -35,7 +35,7 @@ module ConfiguredApp = { /> /* Routing to pages */ {switch url.path { - | list{} => + | list{} => | list{"applications"} => | list{"applications", id} => diff --git a/melon-head/src/App/AppNavigation/styles.module.scss b/melon-head/src/App/AppNavigation/styles.module.scss index d58e590..ca292af 100644 --- a/melon-head/src/App/AppNavigation/styles.module.scss +++ b/melon-head/src/App/AppNavigation/styles.module.scss @@ -14,7 +14,7 @@ flex-direction: column; gap: 6px; margin-top: 64px; - padding: 12px 6px 12px 0; + padding: 12px 6px; } .nav-item { diff --git a/melon-head/src/App/ApplicationDetail.res b/melon-head/src/App/ApplicationDetail.res index aa3949c..dadaaa3 100644 --- a/melon-head/src/App/ApplicationDetail.res +++ b/melon-head/src/App/ApplicationDetail.res @@ -264,11 +264,10 @@ module Actions = { let disabled = Form.MemberNumber.validate(memberNumber)->Option.isSome let doAccept = _ => { - open Json - let body: Js.Json.t = Encode.object([ + let body: Js.Json.t = Json.Encode.object([ ( "member_number", - Encode.option(Encode.int, memberNumber->Option.flatMap(Int.fromString)), + Json.Encode.option(Json.Encode.int, memberNumber->Option.flatMap(Int.fromString)), ), ]) let path = "/applications/" ++ Uuid.toString(id) ++ "/accept" @@ -532,6 +531,90 @@ module Actions = { } } +let viewMessage = (status, ~reject, ~resend) => { + open ApplicationData + + switch status { + | Success(Unverified) => + + {React.string("Email was not verified yet!")} +

+ {React.string( + "This application is in `Verification Pending` status which means it was succefully submitted but applicant never clicked on verify link in the email.", + )} +

+

+ {React.string("These are the recommended steps to take:")} +

+
    +
  1. + {React.string( + "Check when this application was submitted and when the confirmation email was sent using `Metadata` tab.", + )} +
  2. +
  3. + {React.string( + "If application is very recent and there is an entry for when email was sent it might be good to just give applicant more time to verify it.", + )} +
  4. +
  5. + {React.string( + "If we miss information about email being sent that is most likely an issue on our side. Try to ", + )} + {React.string("resend the confirmation email")} + {React.string(" and check after a minute that it was sent successfully.")} +
  6. +
  7. + {React.string( + "If application is already few days old and there is still no confirmation might be good idea to look closer into it:", + )} +
      +
    1. + {React.string(" + You should review the information in the application and check if it's not a spam. + If it is a spam you should + ")} + {React.string("report that we got a spam")} + {React.string(" and ")} + {React.string("reject this application")} + {React.string(" since it's ilegitimate.")} +
    2. +
    3. + {React.string(" + Check if this application is not a duplicate of some other application which is verified. + Applicant might had just found a mistake in this application and decided to create a new one. + If that's the case you should just + ")} + {React.string("reject this version of application")} + {React.string(".")} +
    4. +
    5. + {React.string(" + If everything looks legitimate but you still don't see a verification you can try to + ")} + {React.string("re-send the verification email again")} + {React.string(" + if it looks like there is no problem with the email address. + If that doesn't help it might be a good idea to ")} + {React.string("get in touch with the applicant if possible")} + {React.string(" and try to sort thigs out over phone for example.")} +
    6. +
    7. + {React.string(" + If you're not able to get in touch with applicant and we still don't see any verification + there is probably nothing we can do but to + ")} + {React.string("reject the application")} + {React.string(".")} +
    8. +
    +
  8. +
+
+ | _ => React.null + } +} + type tabs = | Metadata | Checklist @@ -557,6 +640,8 @@ let make = (~id: Uuid.t, ~api: Api.t, ~modal: Modal.Interface.t) => { RescriptReactRouter.push("/applications") } + let status = RemoteData.map(detail, ApplicationData.getStatus) +
@@ -573,9 +658,15 @@ let make = (~id: Uuid.t, ~api: Api.t, ~modal: Modal.Interface.t) => {

{React.string("Status:")} - +

+ {viewMessage( + status, + ~reject=_ => + modal->Modal.Interface.openModal(Actions.rejectModal(~id, ~api, ~setDetail, ~modal)), + ~resend=_ => modal->Modal.Interface.openModal(Actions.resendModal(~id, ~api, ~modal)), + )}
diff --git a/melon-head/src/App/Applications.res b/melon-head/src/App/Applications.res index 64f8751..e49db4f 100644 --- a/melon-head/src/App/Applications.res +++ b/melon-head/src/App/Applications.res @@ -338,11 +338,36 @@ module All = { @react.component let make = (~api: Api.t) => { let tabHandlers = Tabbed.make(Some(ApplicationData.Processing)) + let (_, setActiveTab) = tabHandlers let (basicStats, _, _) = api->Hook.getData(~path="/stats/basic", ~decoder=StatsData.Decode.basic) {React.string("Applications")} + {switch basicStats { + | Success({unverified}) => + if unverified > 0 { + + {React.string("Some applicants might be stuck..")} +

{React.string("Some applications did not pass email verification step yet.")}

+

+ {React.string(" + This might be fine since applicants can always verify email later. + But it also can be the case that some applicants didn't receive the email + or missed the notice about verification altogether. + It might be a good idea to + ")} + setActiveTab(_ => Some(ApplicationData.Unverified))}> + {React.string("check the unverified applications")} + + {React.string(" and make sure this is not the case.")} +

+ + } else { + React.null + } + | _ => React.null + }}
diff --git a/melon-head/src/App/Dashboard.res b/melon-head/src/App/Dashboard.res index cdb6180..1422a5a 100644 --- a/melon-head/src/App/Dashboard.res +++ b/melon-head/src/App/Dashboard.res @@ -26,7 +26,12 @@ let statusRows: array> = [ ] @react.component -let make = (~session: Api.webData, ~setSessionState, ~api: Api.t) => { +let make = ( + ~session: Api.webData, + ~setSessionState, + ~api: Api.t, + ~modal: Modal.Interface.t, +) => { let (basicStats, _, _) = api->Hook.getData(~path="/stats/basic", ~decoder=StatsData.Decode.basic) let (status, _, _) = api->Hook.getData(~path="/status", ~decoder=Api.Decode.status) @@ -86,6 +91,11 @@ let make = (~session: Api.webData, ~setSessionState, ~api: Api.t) => RescriptReactRouter.push("/members/" ++ uuid->Data.Uuid.toString) } + let createMember = _ => { + // TODO: once we have stats for members they should be refreshed there + modal->Modal.Interface.openModal(Members.newMemberModal(~api, ~modal, ~refreshMembers=_ => ())) + } + {React.string("Dashboard")}
@@ -98,16 +108,18 @@ let make = (~session: Api.webData, ~setSessionState, ~api: Api.t) => | Success(None) => - {React.string("Your account is not paired with any member!")} +

{React.string("Your account is not paired with any member!")}

{React.string( - "Some functions won't be accessible unless you pair your account. Just be aware that you might need to create member if it doesn't exist yet.", + "Some functions won't be accessible unless you pair your account. Just be aware that you might need to ", )} + {React.string("create a new member")} + {React.string(" if it doesn't exist yet.")}

{React.string( - "If you're not member but administrator you can safely ignore this message. Fuctions which require membership won't be available to you.", + "If you're not a member, but an administrator, you can safely ignore this message. Fuctions which require membership won't be available to you.", )}

@@ -120,20 +132,28 @@ let make = (~session: Api.webData, ~setSessionState, ~api: Api.t) => )} + {switch error { + | None => React.null + | Some(err) => + + + {React.string("This didin't work as you would wish for...")} + + {React.string(err->Api.showError)} + + }}
| Success(Some(uuid)) => - {React.string("Your account is paired with member id ")} - openMember(uuid)}> - {React.string(Data.Uuid.toString(uuid))} - +

+ {React.string("Your account is paired with member id ")} + openMember(uuid)}> + {React.string(Data.Uuid.toString(uuid))} + +

| _ => React.null }} - {switch error { - | None => React.null - | Some(err) => {React.string(err->Api.showError)} - }}
diff --git a/melon-head/src/App/Members.res b/melon-head/src/App/Members.res index 238ac38..a7076da 100644 --- a/melon-head/src/App/Members.res +++ b/melon-head/src/App/Members.res @@ -60,7 +60,14 @@ module NewMember = { value=newMember.email onInput={email => setNewMember(m => {...m, email})} /> - + + + {React.string("Language")} + setNewMember(m => {...m, language})} + /> + + setNewMember(m => {...m, city})} + value=newMember.postalCode + onInput={postalCode => setNewMember(m => {...m, postalCode})} />