diff --git a/backend/src/uxbox/services/init.clj b/backend/src/uxbox/services/init.clj index 7b7ddf941..02e60dd56 100644 --- a/backend/src/uxbox/services/init.clj +++ b/backend/src/uxbox/services/init.clj @@ -21,6 +21,7 @@ (require 'uxbox.services.queries.files) (require 'uxbox.services.queries.pages) (require 'uxbox.services.queries.profile) + (require 'uxbox.services.queries.recent-files) ;; (require 'uxbox.services.queries.user-attrs) ) diff --git a/backend/src/uxbox/services/queries/projects.clj b/backend/src/uxbox/services/queries/projects.clj index 61dd6edad..75a4376c4 100644 --- a/backend/src/uxbox/services/queries/projects.clj +++ b/backend/src/uxbox/services/queries/projects.clj @@ -20,7 +20,9 @@ (def ^:private sql:projects "with projects as ( - select p.* + select p.*, + (select count(*) from file as f + where f.project_id = p.id and deleted_at is null) as file_count from project as p inner join team_profile_rel as tpr on (tpr.team_id = p.team_id) where tpr.profile_id = $1 @@ -28,7 +30,9 @@ tpr.is_owner = true or tpr.can_edit = true) union - select p.* + select p.*, + (select count(*) from file as f + where f.project_id = p.id and deleted_at is null) from project as p inner join project_profile_rel as ppr on (ppr.project_id = p.id) where ppr.profile_id = $1 @@ -47,7 +51,10 @@ (s/def ::projects-by-team (s/keys :req-un [::profile-id ::team-id])) -(sq/defquery ::projects-by-team - [{:keys [profile-id team-id] :as params}] +(defn projects-by-team [profile-id team-id] (db/query db/pool [sql:projects profile-id team-id])) +(sq/defquery ::projects-by-team + [{:keys [profile-id team-id]}] + (projects-by-team profile-id team-id)) + diff --git a/backend/src/uxbox/services/queries/recent_files.clj b/backend/src/uxbox/services/queries/recent_files.clj new file mode 100644 index 000000000..ef1d59c4d --- /dev/null +++ b/backend/src/uxbox/services/queries/recent_files.clj @@ -0,0 +1,58 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2019-2020 Andrey Antukh + +(ns uxbox.services.queries.recent-files + (:require + [clojure.spec.alpha :as s] + [promesa.core :as p] + [uxbox.db :as db] + [uxbox.common.spec :as us] + [uxbox.services.queries :as sq] + [uxbox.services.queries.projects :refer [ projects-by-team ]] + [uxbox.services.queries.files :refer [ decode-row ]])) + +(def ^:private sql:project_files_recent + "select distinct + f.*, + array_agg(pg.id) over pages_w as pages, + first_value(pg.data) over pages_w as data + from file as f + inner join file_profile_rel as fp_r on (fp_r.file_id = f.id) + left join page as pg on (f.id = pg.file_id) + where fp_r.profile_id = $1 + and f.project_id = $2 + and f.deleted_at is null + and pg.deleted_at is null + and (fp_r.is_admin = true or + fp_r.is_owner = true or + fp_r.can_edit = true) + window pages_w as (partition by f.id order by pg.created_at + range between unbounded preceding + and unbounded following) + order by f.modified_at desc + limit 5") + +(defn recent-by-project [profile-id project] + (let [project-id (:id project)] + (-> (db/query db/pool [sql:project_files_recent profile-id project-id]) + (p/then (partial mapv decode-row))))) + +(s/def ::team-id ::us/uuid) +(s/def ::profile-id ::us/uuid) + +(s/def ::recent-files + (s/keys :req-un [::profile-id ::team-id])) + +(sq/defquery ::recent-files + [{:keys [profile-id team-id]}] + (-> (projects-by-team profile-id team-id) + ;; Retrieve for each proyect the 5 more recent files + (p/then #(p/all (map (partial recent-by-project profile-id) %))) + ;; Change the structure so it's a map with project-id as keys + (p/then #(->> % (flatten) (group-by :project-id))))) diff --git a/frontend/resources/images/icons/recent.svg b/frontend/resources/images/icons/recent.svg new file mode 100644 index 000000000..d9d237fee --- /dev/null +++ b/frontend/resources/images/icons/recent.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index dc1929421..b81419338 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -60,12 +60,14 @@ "en" : "Libraries" } }, + "dashboard.sidebar.recent": "Recent", "dashboard.sidebar.personal" : { "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:104" ], "translations" : { "en" : "Personal" } }, + "dashboard.grid.empty-files": "You still have no files here", "ds.accept" : { "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:113" ], "translations" : { diff --git a/frontend/resources/styles/common/dependencies/helpers.scss b/frontend/resources/styles/common/dependencies/helpers.scss index 4f4816059..dd4447e8f 100644 --- a/frontend/resources/styles/common/dependencies/helpers.scss +++ b/frontend/resources/styles/common/dependencies/helpers.scss @@ -12,6 +12,12 @@ $medium: 15px; $big: 20px; $x-big: 30px; +// New sizes +$size-1: 0.25rem; +$size-2: 0.5rem; +$size-3: 0.8rem; +$size-4: 1rem; + // Border radius $br-small: 3px; $br-medium: 5px; diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index ecb0475a9..8187ee575 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -27,6 +27,8 @@ @import 'common/base'; @import 'main/layouts/main-layout'; @import 'main/layouts/login'; +@import 'main/layouts/projects-page'; +@import 'main/layouts/recent-files-page'; //################################################# // Commons diff --git a/frontend/resources/styles/main/layouts/projects-page.scss b/frontend/resources/styles/main/layouts/projects-page.scss new file mode 100644 index 000000000..d2c7d3bcf --- /dev/null +++ b/frontend/resources/styles/main/layouts/projects-page.scss @@ -0,0 +1,3 @@ +.projects-page { + padding: 1rem; +} diff --git a/frontend/resources/styles/main/layouts/recent-files-page.scss b/frontend/resources/styles/main/layouts/recent-files-page.scss new file mode 100644 index 000000000..ac1146ced --- /dev/null +++ b/frontend/resources/styles/main/layouts/recent-files-page.scss @@ -0,0 +1,43 @@ +.recent-files-page { + overflow: scroll; + height: 100%; +} + +.recent-files-row { + padding: 1rem; + border-top: 1px solid $color-gray-10; + + &:hover { + background-color: white; + } + + &.first { + border-top: none; + } +} + +.recent-files-row-title { + display: flex; + flex-direction: row; + margin-left: $medium; + margin-top: $medium; +} + +.recent-files-row-title-name, .recent-files-row-title-info { + font-size: 15px; + line-height: 1rem; + font-weight: unset; +} + +.recent-files-row-title-name { + color: black; + margin-right: $medium; +} + +.recent-files-row-title-info { +} + +.recent-files-empty { + margin: 30px; + font-size: 20px +} diff --git a/frontend/resources/styles/main/partials/dashboard-grid.scss b/frontend/resources/styles/main/partials/dashboard-grid.scss index d4576687c..fa6e534b2 100644 --- a/frontend/resources/styles/main/partials/dashboard-grid.scss +++ b/frontend/resources/styles/main/partials/dashboard-grid.scss @@ -10,9 +10,6 @@ flex-direction: column; font-size: $fs14; height: 100%; - padding: $medium; - // ACTIVITY BAR PADDING - //padding: $medium 250px $medium $medium; .dashboard-title { margin: $medium 0; @@ -75,15 +72,13 @@ .dashboard-grid-content { display: flex; height: 100%; - min-height: 60vh; - overflow: scroll; width: 100%; .dashboard-grid-row { display: flex; flex-wrap: wrap; - margin-bottom: auto; width: 100%; + align-content: flex-start; } .grid-item { @@ -94,12 +89,28 @@ flex-direction: column; flex-shrink: 0; height: 200px; - margin: $medium $medium 0 $medium; + margin: $medium; max-width: 300px; min-width: 260px; position: relative; text-align: center; width: 18%; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + + & .overlay { + border-radius: 4px; + border: 2px solid $color-primary; + height: 100%; + opacity: 0; + pointer-events: none; + position: absolute; + width: 100%; + z-index: 1; + } + &:hover .overlay { + display: block; + opacity: 1; + } &.small-item { max-width: 12%; @@ -145,9 +156,10 @@ } - &.add-project { + &.add-file { border: 1px dashed $color-gray-light; justify-content: center; + box-shadow: none; span { color: $color-gray-60; @@ -399,5 +411,18 @@ } } - +} + +.grid-files-empty { + align-items: center; + border: 1px dashed $color-gray-20; + display: flex; + flex-direction: column; + margin: $size-4; + padding: 3rem; + width: 100%; +} + +.grid-files-link-text { + color: $color-black; } diff --git a/frontend/resources/styles/main/partials/library-bar.scss b/frontend/resources/styles/main/partials/library-bar.scss index 92452dcb2..d97b08c04 100644 --- a/frontend/resources/styles/main/partials/library-bar.scss +++ b/frontend/resources/styles/main/partials/library-bar.scss @@ -49,7 +49,7 @@ display: flex; flex-direction: column; height: calc(95% - 1rem); - margin-bottom: 1rem; + margin-bottom: $size-4; overflow-y: auto; padding-bottom: 20px; @@ -58,15 +58,14 @@ cursor: pointer; display: flex; flex-shrink: 0; - padding: $medium $small; + padding: $size-4 $size-2; svg { border-radius: 3px; fill: $color-black; - height: 24px; - margin-right: $small; - padding: $x-small; - width: 24px; + margin-right: 8px; + height: $size-4; + width: $size-4; } span.element-title { @@ -81,7 +80,6 @@ border-top: 1px solid $color-gray-10; svg { - background-color: $color-black; fill: $color-white; } @@ -90,22 +88,30 @@ } } + & .edit-wrapper { + display: flex; + } + input.element-title { border: 0; height: 30px; padding: 5px; + margin: 0; + width: 100%; + background-color: $color-white; } .close { - background: $color-gray-50; + background-color: $color-white; cursor: pointer; - padding: 5px 10px; + padding: 3px 5px; svg { fill: $color-gray; - height: 20px; + height: 15px; transform: rotate(45deg) translateY(7px); - width: 20px; + width: 15px; + margin: 0; } } @@ -114,39 +120,20 @@ font-style: italic; } - &:hover { - background-color: $color-primary; + &:hover, + &.current { + background-color: $color-primary-lighter; color: $color-gray-60; } - - &.current { - - svg { - fill: $color-black; - } - - span.element-title, - .element-subtitle { - color: $color-gray-60; - font-weight: bold; - } - input.element-title { - color: $color-gray-dark; - } - } - } - } - } - } .projects-row { align-items: center; display: flex; - padding: $medium; + padding: $size-2; span { color: $color-gray-30; @@ -166,8 +153,8 @@ svg { fill: $color-gray-60; - height: 13px; - width: 13px; + height: $size-2; + width: $size-2; } &:hover { @@ -184,7 +171,7 @@ align-items: center; border: 1px solid $color-gray-10; display: flex; - margin: $medium; + margin: $size-2; .input-text { background: $color-white; diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs index 04cc3d741..b924ca0c5 100644 --- a/frontend/src/uxbox/builtins/icons.cljs +++ b/frontend/src/uxbox/builtins/icons.cljs @@ -74,6 +74,7 @@ (def ungroup (icon-xref :ungroup)) (def unlock (icon-xref :unlock)) (def user (icon-xref :user)) +(def recent (icon-xref :recent)) (def loader-pencil (html diff --git a/frontend/src/uxbox/main/data/dashboard.cljs b/frontend/src/uxbox/main/data/dashboard.cljs index e65b8038f..bc4465116 100644 --- a/frontend/src/uxbox/main/data/dashboard.cljs +++ b/frontend/src/uxbox/main/data/dashboard.cljs @@ -57,9 +57,10 @@ (declare fetch-files) (declare fetch-projects) +(declare fetch-recent-files) (def initialize-drafts - (ptk/reify ::initialize + (ptk/reify ::initialize-drafts ptk/UpdateEvent (update [_ state] (let [profile (:profile state)] @@ -77,7 +78,7 @@ (defn initialize-team [team-id] (us/verify ::us/uuid team-id) - (ptk/reify ::initialize + (ptk/reify ::initialize-team ptk/UpdateEvent (update [_ state] (update state :dashboard-local assoc @@ -87,14 +88,15 @@ ptk/WatchEvent (watch [_ state stream] (let [local (:dashboard-local state)] - (rx/of (fetch-projects (:team-id local))))))) + (rx/of (fetch-projects (:team-id local)) + (fetch-recent-files (:team-id local))))))) (defn initialize-project [team-id project-id] (us/verify ::us/uuid team-id) (us/verify ::us/uuid project-id) - (ptk/reify ::initialize + (ptk/reify ::initialize-project ptk/UpdateEvent (update [_ state] (update state :dashboard-local assoc @@ -156,6 +158,26 @@ files (d/index-by :id files)] (assoc state :files files))))) +;; --- Fetch recent files + +(declare recent-files-fetched) + +(defn fetch-recent-files + [team-id] + (ptk/reify ::fetch-recent-files + ptk/WatchEvent + (watch [_ state stream] + (let [params {:team-id team-id}] + (->> (rp/query :recent-files params) + (rx/map recent-files-fetched)))))) + +(defn recent-files-fetched + [recent-files] + (ptk/reify ::recent-files-fetched + ptk/UpdateEvent + (update [_ state] + (assoc state :recent-files recent-files)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Data Modification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -239,12 +261,12 @@ (declare file-created) -(def create-file +(defn create-file + [project-id] (ptk/reify ::create-draft-file ptk/WatchEvent (watch [_ state stream] (let [name (str "New File " (gensym "p")) - project-id (get-in state [:dashboard-local :project-id]) params {:name name :project-id project-id}] (->> (rp/mutation! :create-file params) (rx/map file-created)))))) @@ -255,7 +277,9 @@ (ptk/reify ::create-draft-file ptk/UpdateEvent (update [this state] - (update state :files assoc (:id data) data)))) + (-> state + (update :files assoc (:id data) data) + (update :recent-files update (:project-id data) conj data))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/uxbox/main/exports.cljs b/frontend/src/uxbox/main/exports.cljs index c52690ef9..f254bc524 100644 --- a/frontend/src/uxbox/main/exports.cljs +++ b/frontend/src/uxbox/main/exports.cljs @@ -25,7 +25,7 @@ {:x 0 :y 0 :width "100%" :height "100%" - :fill "#b1b2b5"}]) + :fill "#AFB2BF"}]) (defn- calculate-dimensions [data] diff --git a/frontend/src/uxbox/main/ui/dashboard.cljs b/frontend/src/uxbox/main/ui/dashboard.cljs index 69b53b040..87438ed31 100644 --- a/frontend/src/uxbox/main/ui/dashboard.cljs +++ b/frontend/src/uxbox/main/ui/dashboard.cljs @@ -19,11 +19,11 @@ [uxbox.main.ui.dashboard.header :refer [header]] [uxbox.main.ui.dashboard.sidebar :refer [sidebar]] [uxbox.main.ui.dashboard.project :refer [project-page]] - [uxbox.main.ui.dashboard.team :refer [team-page]] + [uxbox.main.ui.dashboard.recent-files :refer [recent-files-page]] [uxbox.main.ui.dashboard.profile :refer [profile-section]] [uxbox.main.ui.messages :refer [messages-widget]])) -(defn- ^boolean uuid-str? +(defn ^boolean uuid-str? [s] (and (string? s) (boolean (re-seq us/uuid-rx s)))) @@ -64,7 +64,7 @@ [:& header] (case section :dashboard-team - (mf/element team-page #js {:team-id team-id}) + (mf/element recent-files-page #js {:team-id team-id}) :dashboard-project (mf/element project-page #js {:team-id team-id diff --git a/frontend/src/uxbox/main/ui/dashboard/grid.cljs b/frontend/src/uxbox/main/ui/dashboard/grid.cljs new file mode 100644 index 000000000..92bbc3e4a --- /dev/null +++ b/frontend/src/uxbox/main/ui/dashboard/grid.cljs @@ -0,0 +1,127 @@ +(ns uxbox.main.ui.dashboard.grid + (:refer-clojure :exclude [sort-by]) + (:require + [cuerdas.core :as str] + [rumext.alpha :as mf] + [uxbox.builtins.icons :as i] + [uxbox.main.data.projects :as udp] + [uxbox.main.data.dashboard :as dsh] + [uxbox.main.store :as st] + [uxbox.main.exports :as exports] + [uxbox.main.ui.modal :as modal] + [uxbox.main.ui.keyboard :as kbd] + [uxbox.main.ui.confirm :refer [confirm-dialog]] + [uxbox.util.dom :as dom] + [uxbox.util.i18n :as i18n :refer [t tr]] + [uxbox.util.time :as dt])) + +;; --- Helpers + +(defn sort-by + [ordering files] + (case ordering + :name (cljs.core/sort-by :name files) + :created (reverse (cljs.core/sort-by :created-at files)) + :modified (reverse (cljs.core/sort-by :modified-at files)) + files)) + +(defn contains-term? + [phrase term] + (let [term (name term)] + (str/includes? (str/lower phrase) (str/trim (str/lower term))))) + +(defn filter-by + [term files] + (if (str/blank? term) + files + (filter #(contains-term? (:name %) term) files))) + +;; --- Grid Item Thumbnail + +(mf/defc grid-item-thumbnail + [{:keys [file] :as props}] + [:div.grid-item-th + [:& exports/page-svg {:data (:data file) + :width "290" + :height "150"}]]) + +;; --- Grid Item + +(mf/defc grid-item-metadata + [{:keys [modified-at]}] + (let [locale (i18n/use-locale) + time (dt/timeago modified-at {:locale locale})] + (str (t locale "ds.updated-at" time)))) + +(mf/defc grid-item + {:wrap [mf/wrap-memo]} + [{:keys [file] :as props}] + (let [local (mf/use-state {}) + on-navigate #(st/emit! (udp/go-to (:id file))) + delete-fn #(st/emit! nil (udp/delete-file (:id file))) + on-delete #(do + (dom/stop-propagation %) + (modal/show! confirm-dialog {:on-accept delete-fn})) + + on-blur #(let [name (-> % dom/get-target dom/get-value)] + (st/emit! (udp/rename-file (:id file) name)) + (swap! local assoc :edition false)) + + on-key-down #(when (kbd/enter? %) (on-blur %)) + on-edit #(do + (dom/stop-propagation %) + (dom/prevent-default %) + (swap! local assoc :edition true))] + [:div.grid-item.project-th {:on-click on-navigate} + [:div.overlay] + [:& grid-item-thumbnail {:file file}] + [:div.item-info + (if (:edition @local) + [:input.element-name {:type "text" + :auto-focus true + :on-key-down on-key-down + :on-blur on-blur + ;; :on-click on-edit + :default-value (:name file)}] + [:h3 (:name file)]) + [:& grid-item-metadata {:modified-at (:modified-at file)}]] + [:div.project-th-actions + ;; [:div.project-th-icon.pages + ;; i/page + ;; #_[:span (:total-pages project)]] + ;; [:div.project-th-icon.comments + ;; i/chat + ;; [:span "0"]] + [:div.project-th-icon.edit + {:on-click on-edit} + i/pencil] + [:div.project-th-icon.delete + {:on-click on-delete} + i/trash]]])) + +;; --- Grid + +(mf/defc grid + [{:keys [id opts files hide-new?] :as props}] + (let [locale (i18n/use-locale) + order (:order opts :modified) + filter (:filter opts "") + files (->> files + (filter-by filter) + (sort-by order)) + on-click #(do + (dom/prevent-default %) + (st/emit! (dsh/create-file id)))] + [:section.dashboard-grid + [:div.dashboard-grid-content + (if (> (count files) 0) + [:div.dashboard-grid-row + (when (not hide-new?) + [:div.grid-item.add-file {:on-click on-click} + [:span (tr "ds.new-file")]]) + (for [item files] + [:& grid-item {:file item :key (:id item)}])] + [:div.grid-files-empty + [:div.grid-files-desc (t locale "dashboard.grid.empty-files")] + [:div.grid-files-link + [:a.grid-files-link-text {:on-click on-click} (t locale "ds.new-file")]]])]])) diff --git a/frontend/src/uxbox/main/ui/dashboard/project.cljs b/frontend/src/uxbox/main/ui/dashboard/project.cljs index 73245f942..b65e9f6e3 100644 --- a/frontend/src/uxbox/main/ui/dashboard/project.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/project.cljs @@ -11,154 +11,24 @@ (ns uxbox.main.ui.dashboard.project (:refer-clojure :exclude [sort-by]) (:require - [cuerdas.core :as str] [lentes.core :as l] [rumext.alpha :as mf] - [uxbox.builtins.icons :as i] - [uxbox.main.constants :as c] - [uxbox.main.data.projects :as udp] [uxbox.main.data.dashboard :as dsh] [uxbox.main.store :as st] - [uxbox.main.exports :as exports] - [uxbox.main.refs :as refs] - [uxbox.main.ui.modal :as modal] - [uxbox.main.ui.keyboard :as kbd] - [uxbox.main.ui.confirm :refer [confirm-dialog]] - [uxbox.main.ui.dashboard.header :refer [header]] - [uxbox.main.ui.dashboard.sidebar :refer [sidebar]] - [uxbox.main.ui.messages :refer [messages-widget]] - [uxbox.util.dom :as dom] - [uxbox.util.i18n :as i18n :refer [t tr]] - [uxbox.util.router :as rt] - [uxbox.util.time :as dt])) - - -;; --- Helpers - -(defn sort-by - [ordering files] - (case ordering - :name (cljs.core/sort-by :name files) - :created (reverse (cljs.core/sort-by :created-at files)) - :modified (reverse (cljs.core/sort-by :modified-at files)) - files)) - -(defn contains-term? - [phrase term] - (let [term (name term)] - (str/includes? (str/lower phrase) (str/trim (str/lower term))))) - -(defn filter-by - [term files] - (if (str/blank? term) - files - (filter #(contains-term? (:name %) term) files))) - -;; --- Grid Item Thumbnail - -(mf/defc grid-item-thumbnail - [{:keys [file] :as props}] - [:div.grid-item-th - [:& exports/page-svg {:data (:data file) - :width "290" - :height "150"}]]) - -;; --- Grid Item - -(mf/defc grid-item-metadata - [{:keys [modified-at]}] - (let [locale (i18n/use-locale) - time (dt/timeago modified-at {:locale locale})] - (str (t locale "ds.updated-at" time)))) - -(mf/defc grid-item - {:wrap [mf/wrap-memo]} - [{:keys [file] :as props}] - (let [local (mf/use-state {}) - on-navigate #(st/emit! (udp/go-to (:id file))) - delete-fn #(st/emit! nil (udp/delete-file (:id file))) - on-delete #(do - (dom/stop-propagation %) - (modal/show! confirm-dialog {:on-accept delete-fn})) - - on-blur #(let [name (-> % dom/get-target dom/get-value)] - (st/emit! (udp/rename-file (:id file) name)) - (swap! local assoc :edition false)) - - on-key-down #(when (kbd/enter? %) (on-blur %)) - on-edit #(do - (dom/stop-propagation %) - (dom/prevent-default %) - (swap! local assoc :edition true))] - [:div.grid-item.project-th {:on-click on-navigate} - [:& grid-item-thumbnail {:file file}] - [:div.item-info - (if (:edition @local) - [:input.element-name {:type "text" - :auto-focus true - :on-key-down on-key-down - :on-blur on-blur - ;; :on-click on-edit - :default-value (:name file)}] - [:h3 (:name file)]) - [:& grid-item-metadata {:modified-at (:modified-at file)}]] - [:div.project-th-actions - ;; [:div.project-th-icon.pages - ;; i/page - ;; #_[:span (:total-pages project)]] - ;; [:div.project-th-icon.comments - ;; i/chat - ;; [:span "0"]] - [:div.project-th-icon.edit - {:on-click on-edit} - i/pencil] - [:div.project-th-icon.delete - {:on-click on-delete} - i/trash]]])) - -;; --- Grid - -(mf/defc grid - [{:keys [id opts files] :as props}] - (let [order (:order opts :modified) - filter (:filter opts "") - files (->> files - (filter-by filter) - (sort-by order)) - on-click #(do - (dom/prevent-default %) - (st/emit! dsh/create-file))] - [:section.dashboard-grid - [:div.dashboard-grid-content - [:div.dashboard-grid-row - [:div.grid-item.add-project {:on-click on-click} - [:span (tr "ds.new-file")]] - (for [item files] - [:& grid-item {:file item :key (:id item)}])]]])) - - - -;; --- Component: Project + [uxbox.main.ui.dashboard.grid :refer [grid]])) (def files-ref (-> (comp (l/key :files) (l/lens vals)) (l/derive st/state))) -(def opts-iref - (-> (l/key :dashboard-projects) - (l/derive st/state))) - (mf/defc project-page [{:keys [section team-id project-id] :as props}] - (let [opts (mf/deref opts-iref) - files (mf/deref files-ref)] - + (let [files (mf/deref files-ref)] (mf/use-effect {:fn #(st/emit! (dsh/initialize-project team-id project-id)) :deps (mf/deps team-id project-id)}) - [:section.dashboard-grid.library - [:& grid {:id project-id :opts opts :files files}]])) - + [:section.projects-page + [:& grid { :id project-id :files files }]])) diff --git a/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs new file mode 100644 index 000000000..1b6d213e6 --- /dev/null +++ b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs @@ -0,0 +1,77 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2015-2020 Andrey Antukh +;; Copyright (c) 2015-2020 Juan de la Cruz + +(ns uxbox.main.ui.dashboard.recent-files + (:require + [lentes.core :as l] + [rumext.alpha :as mf] + [uxbox.builtins.icons :as i] + [uxbox.common.exceptions :as ex] + [uxbox.main.constants :as c] + [uxbox.main.data.projects :as udp] + [uxbox.main.data.dashboard :as dsh] + [uxbox.main.store :as st] + [uxbox.main.exports :as exports] + [uxbox.main.refs :as refs] + [uxbox.main.ui.modal :as modal] + [uxbox.main.ui.keyboard :as kbd] + [uxbox.main.ui.confirm :refer [confirm-dialog]] + [uxbox.util.dom :as dom] + [uxbox.util.i18n :as i18n :refer [t tr]] + [uxbox.util.router :as rt] + [uxbox.util.time :as dt] + [uxbox.main.ui.dashboard.grid :refer [grid]]) + ) + +;; --- Component: Content + +(def projects-ref + (-> (l/key :projects) + (l/derive st/state))) + +(def recent-files-ref + (-> (l/key :recent-files) + (l/derive st/state))) + +;; --- Component: Drafts Page + +(mf/defc recent-files-page + [{:keys [section team-id] :as props}] + (mf/use-effect + {:fn #(st/emit! (dsh/initialize-team team-id)) + :deps (mf/deps team-id)}) + (let [projects (mf/deref projects-ref) + recent-files (mf/deref recent-files-ref) + locale (i18n/use-locale)] + (if projects + [:section.recent-files-page + (for [project (vals projects)] + [:div.recent-files-row + {:key (:id project) + :class-name (when (= project (first (vals projects))) "first")} + [:div.recent-files-row-title + [:h2.recent-files-row-title-name (:name project)] + [:span.recent-files-row-title-info (str (:file-count project) " files")] + (when (and recent-files (recent-files (:id project))) + (let [time (-> (project :id) + (recent-files) + (first) + :modified-at + (dt/timeago {:locale locale}))] + [:span.recent-files-row-title-info (str ", " time)]))] + [:& grid {:id (:id project) + :files (or + (and recent-files (recent-files (:id project))) + []) + :hide-new? true}]])] + [:section + [:p "empty"]]) + )) + diff --git a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs index 91d8d2501..f52941336 100644 --- a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs @@ -57,12 +57,14 @@ :on-double-click on-dbl-click :class-name (when selected? "current")} (if (:edit @local) - [:div + [:div.edit-wrapper [:input.element-title {:value (:name @local) :on-change on-input :on-key-down on-keyup}] [:span.close {:on-click on-cancel} i/close]] - [:span.element-title name])])) + [:* + i/folder + [:span.element-title name]])])) (def projects-iref (-> (l/key :projects) @@ -99,8 +101,8 @@ [:li.recent-projects {:on-click #(st/emit! (rt/nav :dashboard-team {:team-id team-id})) :class-name (when home? "current")} - i/user - [:span.element-title (t locale "dashboard.sidebar.personal")]] + i/recent + [:span.element-title (t locale "dashboard.sidebar.recent")]] [:li {:on-click #(st/emit! (rt/nav :dashboard-project {:team-id team-id diff --git a/frontend/src/uxbox/main/ui/dashboard/team.cljs b/frontend/src/uxbox/main/ui/dashboard/team.cljs deleted file mode 100644 index c9de50c97..000000000 --- a/frontend/src/uxbox/main/ui/dashboard/team.cljs +++ /dev/null @@ -1,51 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2015-2020 Andrey Antukh -;; Copyright (c) 2015-2020 Juan de la Cruz - -(ns uxbox.main.ui.dashboard.team - (:require - [lentes.core :as l] - [rumext.alpha :as mf] - [uxbox.builtins.icons :as i] - [uxbox.common.exceptions :as ex] - [uxbox.main.constants :as c] - [uxbox.main.data.projects :as udp] - [uxbox.main.data.dashboard :as dsh] - [uxbox.main.store :as st] - [uxbox.main.exports :as exports] - [uxbox.main.refs :as refs] - [uxbox.main.ui.modal :as modal] - [uxbox.main.ui.keyboard :as kbd] - [uxbox.main.ui.confirm :refer [confirm-dialog]] - [uxbox.util.dom :as dom] - [uxbox.util.i18n :as i18n :refer [t tr]] - [uxbox.util.router :as rt] - [uxbox.util.time :as dt])) - -;; --- Component: Content - -;; (def files-ref -;; (-> (comp (l/key :files) -;; (l/lens vals)) -;; (l/derive st/state))) - -;; (def opts-iref -;; (-> (l/key :dashboard-projects) -;; (l/derive st/state))) - -;; --- Component: Drafts Page - -(mf/defc team-page - [{:keys [section team-id] :as props}] - (mf/use-effect - {:fn #(st/emit! (dsh/initialize-team team-id)) - :deps (mf/deps team-id)}) - - [:section - [:p "TEAM PAGE"]])