0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-09 16:48:16 -05:00

Project and files edition and deletion

This commit is contained in:
Andrés Moya 2020-03-19 16:22:28 +01:00 committed by Andrey Antukh
parent 8b0eab5e90
commit b210e84dd3
9 changed files with 143 additions and 63 deletions

View file

@ -103,12 +103,12 @@
"update file
set name = $2
where id = $1
and deleted_at is null")
and deleted_at is null
returning *")
(defn- rename-file
[conn {:keys [id name] :as params}]
(-> (db/query-one conn [sql:rename-file id name])
(p/then' su/constantly-nil)))
(db/query-one conn [sql:rename-file id name]))
;; --- Mutation: Delete Project File

View file

@ -26,6 +26,7 @@
from project as p
inner join team_profile_rel as tpr on (tpr.team_id = p.team_id)
where tpr.profile_id = $1
and p.deleted_at is null
and (tpr.is_admin = true or
tpr.is_owner = true or
tpr.can_edit = true)
@ -36,6 +37,7 @@
from project as p
inner join project_profile_rel as ppr on (ppr.project_id = p.id)
where ppr.profile_id = $1
and p.deleted_at is null
and (ppr.is_admin = true or
ppr.is_owner = true or
ppr.can_edit = true)

View file

@ -1,18 +1,18 @@
{
"dashboard.grid.delete" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:88" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:73", "src/uxbox/main/ui/dashboard/grid.cljs:91" ],
"translations" : {
"en" : "Delete"
}
},
"dashboard.grid.edit" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:87" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:72", "src/uxbox/main/ui/dashboard/grid.cljs:90" ],
"translations" : {
"en" : "Edit"
}
},
"dashboard.grid.empty-files" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:110" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:113" ],
"translations" : {
"en" : "You still have no files here"
}
@ -25,7 +25,7 @@
"unused" : true
},
"dashboard.header.draft" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:36" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:58" ],
"translations" : {
"en" : "Draft"
}
@ -45,11 +45,17 @@
"unused" : true
},
"dashboard.header.new-file" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:41" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:78" ],
"translations" : {
"en" : "+ New file"
}
},
"dashboard.header.new-project" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/recent_files.cljs:54" ],
"translations" : {
"en" : "+ New project"
}
},
"dashboard.header.profile-menu.logout" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:59", "src/uxbox/main/ui/workspace/header.cljs:93" ],
"translations" : {
@ -72,7 +78,7 @@
}
},
"dashboard.header.project" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:37" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:74" ],
"translations" : {
"en" : "Project %s"
}
@ -374,7 +380,7 @@
"unused" : true
},
"ds.new-file" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:106", "src/uxbox/main/ui/dashboard/grid.cljs:112" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:109", "src/uxbox/main/ui/dashboard/grid.cljs:115" ],
"translations" : {
"en" : "+ New File",
"fr" : null
@ -451,7 +457,7 @@
}
},
"ds.updated-at" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:34" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:35" ],
"translations" : {
"en" : "Updated: %s",
"fr" : "Mis à jour: %s"
@ -528,14 +534,14 @@
}
},
"errors.generic" : {
"used-in" : [ "src/uxbox/main/ui.cljs:154" ],
"used-in" : [ "src/uxbox/main/ui.cljs:160" ],
"translations" : {
"en" : "Something wrong has happened.",
"fr" : "Quelque chose c'est mal passé."
}
},
"errors.network" : {
"used-in" : [ "src/uxbox/main/ui.cljs:148" ],
"used-in" : [ "src/uxbox/main/ui.cljs:154" ],
"translations" : {
"en" : "Unable to connect to backend server.",
"fr" : "Impossible de se connecter au serveur principal."

View file

@ -272,6 +272,10 @@
}
.project-th-actions.force-display {
display: flex;
}
}
// IMAGES SECTION

View file

@ -15,11 +15,22 @@
position: relative;
z-index: 10;
.element-name {
margin-right: $small;
}
.btn-dashboard {
flex-shrink: 0;
margin-left: auto;
}
svg {
fill: $color-black;
height: 14px;
margin-right: $x-small;
width: 14px;
}
}
.main-logo {
@ -70,6 +81,7 @@
.dashboard-title {
color: $color-black;
display: flex;
font-size: $fs15;
font-weight: normal;
margin: 0 $x-big;

View file

@ -88,7 +88,7 @@
(watch [_ state stream]
(let [local (:dashboard-local state)]
(rx/of (fetch-files (:project-id local))
(fetch-projects (:team-id local) (:project-id local)))))))
(fetch-projects (:team-id local)))))))
(defn initialize-recent
@ -104,7 +104,7 @@
ptk/WatchEvent
(watch [_ state stream]
(let [local (:dashboard-local state)]
(rx/of (fetch-projects (:team-id local) (:project-id nil))
(rx/of (fetch-projects (:team-id local))
(fetch-recent-files (:team-id local)))))))
@ -122,7 +122,7 @@
ptk/WatchEvent
(watch [_ state stream]
(let [local (:dashboard-local state)]
(rx/of (fetch-projects (:team-id local) (:project-id local))
(rx/of (fetch-projects (:team-id local))
(fetch-files (:project-id local)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -141,21 +141,15 @@
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query :projects-by-team {:team-id team-id})
(rx/map (projects-fetched project-id))))))
(rx/map projects-fetched)))))
(defn projects-fetched
[project-id]
(us/assert (s/nilable ::us/uuid) project-id)
(fn [projects]
(us/verify (s/every ::project) projects)
(ptk/reify ::projects-fetched
ptk/UpdateEvent
(update [_ state]
(let [find-project #(first (filter (fn [p] (= (:id p) %1)) projects))
set-project #(assoc %1 :project (find-project project-id))
assoc-project #(assoc-in %1 [:projects (:id %2)] %2)
reduce-projects #(reduce assoc-project %1 projects)]
(-> state set-project reduce-projects))))))
[projects]
(us/verify (s/every ::project) projects)
(ptk/reify ::projects-fetched
ptk/UpdateEvent
(update [_ state]
(assoc state :projects (d/index-by :id projects)))))
;; --- Search Files
@ -221,7 +215,17 @@
(ptk/reify ::recent-files-fetched
ptk/UpdateEvent
(update [_ state]
(assoc state :recent-files recent-files))))
(let [flatten-files #(reduce (fn [acc [project-id files]]
(merge acc (d/index-by :id files)))
{}
%1)
extract-ids #(reduce (fn [acc [project-id files]]
(assoc acc project-id (map :id files)))
{}
%1)]
(assoc state
:files (flatten-files recent-files)
:recent-file-ids (extract-ids recent-files))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Modification
@ -229,6 +233,8 @@
;; --- Create Project
(declare project-created)
(def create-project
(ptk/reify ::create-project
ptk/WatchEvent
@ -236,8 +242,15 @@
(let [name (str "New Project " (gensym "p"))
team-id (get-in state [:dashboard-local :team-id])]
(->> (rp/mutation! :create-project {:name name :team-id team-id})
(rx/map (fn [data]
(projects-fetched [data]))))))))
(rx/map project-created))))))
(defn project-created
[data]
(us/verify ::project data)
(ptk/reify ::project-created
ptk/UpdateEvent
(update [this state]
(update state :projects assoc (:id data) data))))
;; --- Rename Project
@ -285,7 +298,7 @@
(->> (rp/mutation :delete-file {:id id})
(rx/ignore)))))
;; --- Rename Project
;; --- Rename File
(defn rename-file
[id name]
@ -299,11 +312,8 @@
(watch [_ state stream]
(let [local (:dashboard-local state)
params {:id id :name name}]
;; NOTE: this is a temporal (quick & dirty) solution for
;; refreshing the results; we need to think in a better way to
;; do it instead of a simple and complete data refresh.
(->> (rp/mutation :rename-file params)
(rx/map (fn [_] (initialize-recent (:team-id local)))))))))
(rx/ignore))))))
;; --- Create File
@ -312,7 +322,7 @@
(defn create-file
[project-id]
(ptk/reify ::create-draft-file
(ptk/reify ::create-file
ptk/WatchEvent
(watch [_ state stream]
(let [name (str "New File " (gensym "p"))
@ -323,12 +333,10 @@
(defn file-created
[data]
(us/verify ::file data)
(ptk/reify ::create-draft-file
(ptk/reify ::file-created
ptk/UpdateEvent
(update [this state]
(-> state
(update :files assoc (:id data) data)
(update-in [:recent-files (:project-id data)] conj data)))))
(update state :files assoc (:id data) data))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -11,6 +11,7 @@
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.components.context-menu :refer [context-menu]]
[uxbox.util.data :refer [classnames]]
[uxbox.util.dom :as dom]
[uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.router :as rt]
@ -51,7 +52,9 @@
(st/emit! (dsh/rename-file (:id file) name))
(swap! local assoc :edition false))
on-key-down #(when (kbd/enter? %) (on-blur %))
on-key-down #(cond
(kbd/enter? %) (on-blur %)
(kbd/esc? %) (swap! local assoc :edition false))
on-menu-click #(do
(dom/stop-propagation %)
(swap! local assoc :menu-open true))
@ -68,11 +71,11 @@
: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-actions {:class (classnames
:force-display (:menu-open @local))}
;; [:div.project-th-icon.pages
;; i/page
;; #_[:span (:total-pages project)]]

View file

@ -12,14 +12,20 @@
(:require
[lentes.core :as l]
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.util.dom :as dom]
[uxbox.util.router :as rt]
[uxbox.main.data.dashboard :as dsh]
[uxbox.main.store :as st]
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.components.context-menu :refer [context-menu]]
[uxbox.main.ui.dashboard.grid :refer [grid]]))
(def project-ref
(-> (l/key :project)
(def projects-ref
(-> (l/key :projects)
(l/derive st/state)))
(def files-ref
@ -28,16 +34,47 @@
(l/derive st/state)))
(mf/defc project-header
[{:keys [profile] :as props}]
(let [project (mf/deref project-ref)
locale (i18n/use-locale)]
[{:keys [team-id project-id] :as props}]
(let [local (mf/use-state {:menu-open false
:edition false})
projects (mf/deref projects-ref)
project (get projects project-id)
locale (i18n/use-locale)
on-menu-click #(swap! local assoc :menu-open true)
on-menu-close #(swap! local assoc :menu-open false)
on-edit #(swap! local assoc :edition true :menu-open false)
on-blur #(let [name (-> % dom/get-target dom/get-value)]
(st/emit! (dsh/rename-project project-id name))
(swap! local assoc :edition false))
on-key-down #(cond
(kbd/enter? %) (on-blur %)
(kbd/esc? %) (swap! local assoc :edition false))
delete-fn #(do
(st/emit! (dsh/delete-project project-id))
(st/emit! (rt/nav :dashboard-team {:team-id team-id})))
on-delete #(modal/show! confirm-dialog {:on-accept delete-fn})]
[:header#main-bar.main-bar
(if (:is-default project)
[:h1.dashboard-title (t locale "dashboard.header.draft")]
[:h1.dashboard-title (t locale "dashboard.header.project" (:name project))])
[:*
(if (:edition @local)
[:input.element-name {:type "text"
:auto-focus true
:on-key-down on-key-down
:on-blur on-blur
:default-value (:name project)}]
[:h1.dashboard-title
[:div.main-bar-icon
{:on-click on-menu-click}
i/actions]
[:& context-menu {:on-close on-menu-close
:show (:menu-open @local)
:options [[(t locale "dashboard.grid.edit") on-edit]
[(t locale "dashboard.grid.delete") on-delete]]}]
(t locale "dashboard.header.project" (:name project))])])
[:a.btn-dashboard {:on-click #(do
(dom/prevent-default %)
(st/emit! (dsh/create-file (:id project))))}
(st/emit! (dsh/create-file project-id)))}
(t locale "dashboard.header.new-file")]]))
(mf/defc project-page
@ -47,10 +84,10 @@
(reverse))]
(mf/use-effect
{:fn #(st/emit! (dsh/initialize-project team-id project-id))
:deps (mf/deps team-id project-id)})
:deps (mf/deps section team-id project-id)})
[:*
[:& project-header]
[:& project-header {:team-id team-id :project-id project-id}]
[:section.projects-page
[:& grid { :id project-id :files files :hide-new? true}]]]))

View file

@ -35,8 +35,12 @@
(-> (l/key :projects)
(l/derive st/state)))
(def recent-files-ref
(-> (l/key :recent-files)
(def recent-file-ids-ref
(-> (l/key :recent-file-ids)
(l/derive st/state)))
(def files-ref
(-> (l/key :files)
(l/derive st/state)))
;; --- Component: Recent files
@ -46,7 +50,8 @@
(let [locale (i18n/use-locale)]
[:header#main-bar.main-bar
[:h1.dashboard-title "Recent"]
[:a.btn-dashboard "+ New project"]]))
[:a.btn-dashboard {:on-click #(st/emit! dsh/create-project)}
(t locale "dashboard.header.new-project")]]))
(mf/defc recent-project
[{:keys [project files first? locale] :as props}]
@ -60,12 +65,12 @@
(dt/timeago {:locale locale}))]
[:span.recent-files-row-title-info (str ", " time)])]
[:& grid {:id (:id project)
:files (or files [])
:files files
:hide-new? true}]]))
(mf/defc recent-files-page
[{:keys [section team-id] :as props}]
[{:keys [team-id] :as props}]
(mf/use-effect
{:fn #(st/emit! (dsh/initialize-recent team-id))
:deps (mf/deps team-id)})
@ -73,10 +78,11 @@
(vals)
(sort-by :modified-at)
(reverse))
recent-files (mf/deref recent-files-ref)
files (mf/deref files-ref)
recent-file-ids (mf/deref recent-file-ids-ref)
locale (i18n/use-locale)]
(when (and projects recent-files)
(when (and projects recent-file-ids)
[:*
[:& recent-files-header]
[:section.recent-files-page
@ -84,6 +90,8 @@
[:& recent-project {:project project
:locale locale
:key (:id project)
:files (get recent-files (:id project))
:files (->> (get recent-file-ids (:id project))
(map #(get files %))
(filter identity)) ;; avoid failure if a "project only" files list is in global state
:first? (= project (first projects))}])]])))