Repository: CSCfi/rems (clojure)

Issue:
vTHL4: Show submitted instead of created timestamp to handler
THL wants the handler to see the submitted timestamp in tables, instead of the created timestamp. It's not useful for the handler to see when the applicant started creating the application but when it was sent.

- [x] Show two timestamp columns on the actions page: submitted and last activity instead of created and last activity.

Pull request description:
Closes #1229 

- Shows submitted for applicant, reporter and handler. For the applicant it's shown in addition to created.
- Submitted is tracked from the very first submit. 
- Refactors `application-list/component` to remove extra layers.
- Fix the heading to be h1. That's the future with A11Y

Patch:
diff --git a/resources/translations/en.edn b/resources/translations/en.edn
index 291499262d..57a7dc633a 100644
--- a/resources/translations/en.edn
+++ b/resources/translations/en.edn
@@ -89,7 +89,8 @@
             :return "Return to applicant"
             :show-publications "Show publications"
             :show-throughput-times "Show throughput times"
-            :state "State"}
+            :state "State"
+            :submitted "Sent"}
   :administration {:active "Active"
                    :archive "Archive"
                    :back "Back"
@@ -142,6 +143,7 @@
                    :username "Username"}
   :applications {:all-applications "All Applications"
                  :application "Application"
+                 :applications "Applications"
                  :empty "You don't have any applications."
                  :events {:approved "Approved"
                           :closed "Deleted"
diff --git a/resources/translations/fi.edn b/resources/translations/fi.edn
index 4fff7052b8..853fe7526a 100644
--- a/resources/translations/fi.edn
+++ b/resources/translations/fi.edn
@@ -89,7 +89,8 @@
             :return "Palauta hakijalle"
             :show-publications "Näytä julkaisut"
             :show-throughput-times "Näytä läpimenoajat"
-            :state "Tila"}
+            :state "Tila"
+            :submitted "Lähtetetty"}
   :administration {:active "Aktiivinen"
                    :archive "Arkistoi"
                    :back "Takaisin"
@@ -142,6 +143,7 @@
                    :username "Käyttäjätunnus"}
   :applications {:all-applications "Kaikki hakemukset"
                  :application "Hakemus"
+                 :applications "Hakemukset"
                  :empty "Sinulla ei ole hakemuksia."
                  :events {:approved "Hyväksytty"
                           :closed "Poistettu"
diff --git a/src/clj/rems/api/schema.clj b/src/clj/rems/api/schema.clj
index e2222cf5f5..987a146bfb 100644
--- a/src/clj/rems/api/schema.clj
+++ b/src/clj/rems/api/schema.clj
@@ -135,6 +135,7 @@
    :application/state s/Keyword
    :application/created DateTime
    :application/modified DateTime
+   (s/optional-key :application/first-submitted) DateTime
    :application/last-activity DateTime
    :application/applicant s/Str
    :application/applicant-attributes {s/Keyword s/Str}
diff --git a/src/clj/rems/application/model.clj b/src/clj/rems/application/model.clj
index 2a5cb7b4a7..b4a75ab0b9 100644
--- a/src/clj/rems/application/model.clj
+++ b/src/clj/rems/application/model.clj
@@ -249,6 +249,7 @@
   (-> application
       (assoc ::previous-submitted-answers (::submitted-answers application))
       (assoc ::submitted-answers (::draft-answers application))
+      (update :application/first-submitted #(or % (:event/time event)))
       (dissoc ::draft-answers)
       (assoc :application/state :application.state/submitted)))
 
diff --git a/src/cljs/rems/actions.cljs b/src/cljs/rems/actions.cljs
index 1f7f028672..13eaf86036 100644
--- a/src/cljs/rems/actions.cljs
+++ b/src/cljs/rems/actions.cljs
@@ -119,9 +119,8 @@
   (if (empty? apps)
     [:div.actions.alert.alert-success (text :t.actions/empty)]
     [application-list/component
-     {:visible-columns
-      (application-list/open-application-visible-columns
-       (get @(rf/subscribe [:rems.config/config]) :application-id-column :id))
+     {:visible-columns (into [(get @(rf/subscribe [:rems.config/config]) :application-id-column :id)]
+                             [:description :resource :applicant :state :submitted :last-activity :view])
       :sorting (assoc @(rf/subscribe [::sorting ::open-applications])
                       :set-sorting #(rf/dispatch [::set-sorting ::open-applications %]))
       :filtering (assoc @(rf/subscribe [::filtering ::open-applications])
@@ -143,9 +142,8 @@
       [:div
        top-buttons
        [application-list/component
-        {:visible-columns
-         (application-list/handled-application-visible-columns
-          (get @(rf/subscribe [:rems.config/config]) :application-id-column :id))
+        {:visible-columns (into [(get @(rf/subscribe [:rems.config/config]) :application-id-column :id)]
+                                [:description :resource :applicant :state :last-activity :view])
          :sorting (assoc @(rf/subscribe [::sorting ::handled-applications])
                          :set-sorting #(rf/dispatch [::set-sorting ::handled-applications %]))
          :filtering (assoc @(rf/subscribe [::filtering ::handled-applications])
diff --git a/src/cljs/rems/application_list.cljs b/src/cljs/rems/application_list.cljs
index 692b3eff00..2ba0108d9a 100644
--- a/src/cljs/rems/application_list.cljs
+++ b/src/cljs/rems/application_list.cljs
@@ -22,51 +22,6 @@
          :title (:application/description app)}
    (:application/description app)])
 
-(def ^:private +open-application-base-columns+
-  [:description :resource :applicant :state :created :last-activity :view])
-
-(def ^:private +handled-application-base-columns+
-  [:description :resource :applicant :state :last-activity :view])
-
-(def +draft-columns+
-  [:resource :last-activity :view])
-
-(defn open-application-visible-columns
-  "Given the database column name shown as id, return a vector of columns
-  visible in the application list for open applications."
-  [id-column]
-  (vec (cons id-column +open-application-base-columns+)))
-
-(defn handled-application-visible-columns
-  "Given the database column name shown as id, return a vector of columns
-  visible in the application list for handled applications."
-  [id-column]
-  (vec (cons id-column +handled-application-base-columns+)))
-
-(def ^:private +columns+
-  {:external-id {:value :application/external-id
-                 :header #(text :t.actions/id)}
-   :id {:value :application/id
-        :header #(text :t.actions/id)}
-   :description {:value format-description
-                 :header #(text :t.actions/description)}
-   :resource {:value format-catalogue-items
-              :header #(text :t.actions/resource)}
-   :applicant {:value :application/applicant
-               :header #(text :t.actions/applicant)}
-   :state {:value #(localize-state (:application/state %))
-           :header #(text :t.actions/state)
-           :class #(if (application-util/form-fields-editable? %) "state text-highlight" "state")}
-   :created {:value #(localize-time (:application/created %))
-             :sort-value :application/created
-             :header #(text :t.actions/created)}
-   :last-activity {:value #(localize-time (:application/last-activity %))
-                   :sort-value :application/last-activity
-                   :header #(text :t.actions/last-activity)}
-   :view {:value view-button
-          :sortable? false
-          :filterable? false}})
-
 (defn component
   "A table of applications.
 
@@ -75,7 +30,31 @@
   Binds the column definitions for you and the visible columns should be a subsequence."
   [opts]
   [table/component
-   (merge {:column-definitions +columns+
+   (merge {:column-definitions {:external-id {:value :application/external-id
+                                              :header #(text :t.actions/id)}
+                                :id {:value :application/id
+                                     :header #(text :t.actions/id)}
+                                :description {:value format-description
+                                              :header #(text :t.actions/description)}
+                                :resource {:value format-catalogue-items
+                                           :header #(text :t.actions/resource)}
+                                :applicant {:value :application/applicant
+                                            :header #(text :t.actions/applicant)}
+                                :state {:value #(localize-state (:application/state %))
+                                        :header #(text :t.actions/state)
+                                        :class #(if (application-util/form-fields-editable? %) "state text-highlight" "state")}
+                                :created {:value #(localize-time (:application/created %))
+                                          :sort-value :application/created
+                                          :header #(text :t.actions/created)}
+                                :submitted {:value #(localize-time (:application/first-submitted %))
+                                            :sort-value :application/first-submitted
+                                            :header #(text :t.actions/submitted)}
+                                :last-activity {:value #(localize-time (:application/last-activity %))
+                                                :sort-value :application/last-activity
+                                                :header #(text :t.actions/last-activity)}
+                                :view {:value view-button
+                                       :sortable? false
+                                       :filterable? false}}
            :id-function #(str "application-" (:application/id %))
            :class "applications"}
           opts)])
@@ -118,19 +97,19 @@
   [:div
    (component-info component)
    (example "empty list"
-            [component {:visible-columns (open-application-visible-columns :id)
+            [component {:visible-columns [:id :description :resource :applicant :state :created :last-activity :view]
                         :sorting {:sort-column :id :sort-order :asc}
                         :items []}])
    (example "applications, default order"
-            [component {:visible-columns (open-application-visible-columns :id)
+            [component {:visible-columns [:id :description :resource :applicant :state :created :last-activity :view]
                         :sorting {:sort-column :id :sort-order :asc}
                         :items +example-applications+}])
    (example "applications, descending date, all columns"
-            [component {:visible-columns (open-application-visible-columns :id)
+            [component {:visible-columns [:id :description :resource :applicant :state :created :last-activity :view]
                         :sorting {:sort-column :created :sort-order :desc}
                         :items +example-applications+}])
    (example "applications, initially sorted by id descending, then resource descending"
-            [component {:visible-columns (open-application-visible-columns :id)
+            [component {:visible-columns [:id :description :resource :applicant :state :created :last-activity :view]
                         :sorting {:initial-sort [{:sort-column :id :sort-order :desc}
                                                  {:sort-column :resource :sort-order :desc}]}
                         :items +example-applications+}])])
diff --git a/src/cljs/rems/applications.cljs b/src/cljs/rems/applications.cljs
index ed39d38579..f6cf035a25 100644
--- a/src/cljs/rems/applications.cljs
+++ b/src/cljs/rems/applications.cljs
@@ -93,9 +93,8 @@
 
         :else
         [application-list/component
-         {:visible-columns
-          (application-list/open-application-visible-columns
-           (get @(rf/subscribe [:rems.config/config]) :application-id-column :id))
+         {:visible-columns (into [(get @(rf/subscribe [:rems.config/config]) :application-id-column :id)]
+                                 [:description :resource :state :created :submitted :last-activity :view])
           :sorting (assoc @(rf/subscribe [::sorting])
                           :set-sorting #(rf/dispatch [::set-sorting %]))
           :filtering (assoc @(rf/subscribe [::filtering])
@@ -104,12 +103,14 @@
 
 (defn applications-page []
   (let [apps @(rf/subscribe [::my-applications])
+        identity @(rf/subscribe [:identity])
         loading? @(rf/subscribe [::loading-my-applications?])]
     [:div
-     [:h2 (text :t.applications/my-applications)]
+     [:h1 (text :t.applications/applications)]
+     (when (roles/show-all-applications? (:roles identity))
+       [:h2 (text :t.applications/my-applications)])
      [application-list apps loading?]
-     (let [identity @(rf/subscribe [:identity])
-           apps @(rf/subscribe [::all-applications])
+     (let [apps @(rf/subscribe [::all-applications])
            loading? @(rf/subscribe [::loading-all-applications?])]
        (when (roles/show-all-applications? (:roles identity))
          [:div
diff --git a/src/cljs/rems/catalogue.cljs b/src/cljs/rems/catalogue.cljs
index 1757897180..dc75bea9c6 100644
--- a/src/cljs/rems/catalogue.cljs
+++ b/src/cljs/rems/catalogue.cljs
@@ -119,8 +119,7 @@
     [:div.drafts
      [:h4 (text :t.catalogue/continue-existing-application)]
      [application-list/component
-      ;; TODO: use application-list/visible-columns like on other pages?
-      {:visible-columns application-list/+draft-columns+
+      {:visible-columns [:resource :last-activity :view]
        :items drafts}]]))
 
 (defn catalogue-page []


Test patch:
diff --git a/test/clj/rems/application/test_model.clj b/test/clj/rems/application/test_model.clj
index 1e86c2a028..ba6b8643ac 100644
--- a/test/clj/rems/application/test_model.clj
+++ b/test/clj/rems/application/test_model.clj
@@ -379,6 +379,7 @@
                         expected-application (merge expected-application
                                                     {:application/last-activity (DateTime. 3000)
                                                      :application/events events
+                                                     :application/first-submitted (DateTime. 3000)
                                                      :application/state :application.state/submitted})]
                     (is (= expected-application (apply-events events)))
 
diff --git a/test/clj/rems/test_browser.clj b/test/clj/rems/test_browser.clj
index af7357ed22..3a2b09c573 100644
--- a/test/clj/rems/test_browser.clj
+++ b/test/clj/rems/test_browser.clj
@@ -95,7 +95,7 @@
 
 (defn go-to-applications []
   (click-navigation-menu "Applications")
-  (wait-visible *driver* {:tag :h2, :fn/text "My Applications"})
+  (wait-visible *driver* {:tag :h1, :fn/text "Applications"})
   (wait-visible *driver* [{:css "i.fa-search"}])
   (wait-page-loaded)
   (screenshot *driver* (io/file reporting-dir "applications-page.png")))
@@ -189,7 +189,6 @@
     {:id application-id
      :description (get-element-text-el *driver* (child *driver* row {:css ".description"}))
      :resource (get-element-text-el *driver* (child *driver* row {:css ".resource"}))
-     :applicant (get-element-text-el *driver* (child *driver* row {:css ".applicant"}))
      :state (get-element-text-el *driver* (child *driver* row {:css ".state"}))}))
 
 ;;; tests
@@ -232,7 +231,6 @@
       (go-to-applications)
       (let [summary (get-application-summary application-id)]
         (is (= "THL catalogue item" (:resource summary)))
-        (is (= "alice" (:applicant summary)))
         (is (= "Applied" (:state summary)))
         ;; don't bother trying to predict the external id:
         (is (.contains (:description summary) "Test name"))))))