Skip to content

Instantly share code, notes, and snippets.

@Alex-Bakic
Last active November 25, 2019 12:08
Show Gist options
  • Save Alex-Bakic/7a9e91b16bfcba4de2b4b9a017455492 to your computer and use it in GitHub Desktop.
Save Alex-Bakic/7a9e91b16bfcba4de2b4b9a017455492 to your computer and use it in GitHub Desktop.

Cancelling the application process

The aim is to allow the candidate to click on an "x" button within a job card on the applied page, and have that process terminated.

The checks that must be ran before showing this close icon is the following:

- make sure the user is logged-in
- make sure on-close is nil, as if it is not nil, it means that we are on a page that should
  be reloading likes. Would need to then tweak applied page to reload only for closing jobs, and not liking.
  We will need to reload, though this is done within the event chain itself, saving us from tweaking the ::toggle-like event.
- make sure that the job has been applied to ...

All these culminate in the following and within job.cljc:

 (when (and logged-in? (not on-close) applied?)
   [icon "close"
    ;; keep same class names for now...
    :id (str "job-card__blacklist-button_job-" id)
    :class "job__icon blacklist"
    :on-click #(dispatch [::cancel/start-cancellation job])])

I gave the cancel-application issue it's own folder within apply, as it needs it's own db, events , views etc and it shouldn't cramp what's in apply. The db is structured in much a similiar way as to apply , but we need to include a reason for why the candidate decided to cancel. For now I have only listed "changed mind" "found work", with the input field handling the rest.

;; within apply.cancel-apply.db

;; helper for checking reason
(defn check-reason
  [defaults reason]
  (let [defaults #{"found work" "changed mind"}]
    (if (some #{reason} defaults)
      reason
      (s/conform ::other-reasons reason))))

(def steps #{:reason :thanks})
(def default-reasons #{"found work" "changed mind"})
(s/def ::steps-taken (s/coll-of steps))
...
(s/def ::other-reasons string?)
(s/def ::reason (partial check-reason #{"found work" "changed mind"}))
(s/def ::reason-failed? boolean?)

The reason they're strings instead of keywords is to allow for the spec to easily validate "other" reasons, which wouldn't be keywordized. I thought about having a map of {::cancel-db/reason {::cancel-db/other "the reason"}} but it overcomplicates things. We can just introduce more checks over the string if necessary.

As for the events , this is the chain:

- user clicks the "x" on the job card, which triggers an event , ::start-cancellation, 
  that sets the current step, and assocs the job to be removed into the sub-db.

Now I've currently got the default reasons as conversation buttons, as they need to trigger an event which adds the reason to the db, so they need to be a bit more dynamic than checkboxes, and once clicked they change colour to signify that the button is used over the text input. But when the user would start typing , then that reason will be used instead and it overrides the key, changing the colour back. It may be better to have a select field, as the reasons grow, but currently there are only two defaults, and the buttons are more expressive than either a checkbox or select field.

- once the button is clicked or the user types in the input , it should call ::set-reason which
  assocs either a default or the text into ::cancel-db/reason.
- only when the user clicks submit does the next chain of events start firing
- when submit is clicked the job should be pulled from db, and graphql should try and remove it.
- on-success we will want to remove the job from the sub-db , and also from :user.sub-db/applied-jobs,
  so as not to mark jobs as applied. We'll also want to mark the current step as :thanks if it was successful,
  otherwise allow the user to try again.

And for the views, we just need to wire up these events correctly, putting the ::set-reason within each button:

[:div.animatable
        [:button.conversation-button
         (when (= :reason (<sub [::subs/current-step]))
           ;; check it matches the default , as the text input may override
           ;; all the subs are very simple, boring watchers of the ::cancel/sub-db path.
           {:class   (when (= "found work" (<sub [::subs/reason])) "button--has-reason")
            :id       "application-bot_update-reason"
            :on-click #(dispatch [::events/set-reason "found work"])})
         "I've found work"]]

And for the text input:

 [:div.animatable
    [:div.conversation-element.user
     [:input {:type :text
              :auto-focus true
              :placeholder "other"
              ;; reason is initially nil so placeholder is shown
              :value @reason
              :on-change  #(do (reset! reason (-> % .-target .-value))
                               (dispatch [::events/set-reason @reason])
                               (reset! validation-error? false))
              :aria-label "Reason input"}]]]

You can click here for the full view:

And to see what the overlay looks like when you click "x" :

cancel-no-click

And on-click:

cancel-on-click

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment