Compare commits

..

7 Commits

Author SHA1 Message Date
Andrey Antukh
1beb3b86aa 🐛 Add missing msgid_plural on several translations 2026-01-12 11:52:07 +01:00
Andrey Antukh
fe20bdd00e 🐛 Fix multiple selection options on dashboard deleted page (#8055)
* 🐛 Fix multiple selection options on dashboard deleted page

* 📎 Fix translations
2026-01-12 11:41:37 +01:00
María Valderrama
5420897b92 🐛 Fix empty state message in trash page (#8045) 2026-01-12 11:17:01 +01:00
Andrey Antukh
e430a4c9f3 Revert " Backport translations from develop"
This reverts commit ec6d72bd91.
2026-01-12 10:40:17 +01:00
Andrey Antukh
ec6d72bd91 Backport translations from develop 2026-01-12 09:39:33 +01:00
Eva Marco
6ec451b46d 🐛 Fix resolved value on line height (#8047) 2026-01-09 12:54:24 +01:00
Andrey Antukh
2b836f10cb 🐛 Do not show deleted files on search (#8036)
* 🐛 Do not show deleted files on search

* 💄 Add cosmetic changes to dashboard deleted files page
2026-01-09 11:11:29 +01:00
12 changed files with 137 additions and 103 deletions

View File

@@ -19,7 +19,7 @@
inner join team_profile_rel as tpr on (tpr.team_id = p.team_id)
where tpr.profile_id = ?
and p.team_id = ?
and (p.deleted_at is null or p.deleted_at > now())
and (p.deleted_at is null)
and (tpr.is_admin = true or
tpr.is_owner = true or
tpr.can_edit = true)
@@ -29,7 +29,7 @@
inner join project_profile_rel as ppr on (ppr.project_id = p.id)
where ppr.profile_id = ?
and p.team_id = ?
and (p.deleted_at is null or p.deleted_at > now())
and (p.deleted_at is null)
and (ppr.is_admin = true or
ppr.is_owner = true or
ppr.can_edit = true)
@@ -47,7 +47,7 @@
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn)
inner join projects as pr on (f.project_id = pr.id)
where f.name ilike ('%' || ? || '%')
and (f.deleted_at is null or f.deleted_at > now())
and (f.deleted_at is null)
order by f.created_at asc")
(defn search-files

View File

@@ -20,12 +20,7 @@ test.describe("Dashboard Deleted Page", () => {
// Navigate directly to deleted page
await dashboardPage.goToDeleted();
// Check for the restore all and clear trash buttons
await expect(
page.getByRole("button", { name: "Restore All" }),
).toBeVisible();
await expect(
page.getByRole("button", { name: "Clear trash" }),
).toBeVisible();
// Check for the delete-page-section element
await expect(page.getByTestId("deleted-page-section")).toBeVisible();
});
});

View File

@@ -13,7 +13,7 @@
$weight: unquote("normal"),
$style: string.unquote("normal")
) {
$filepath: "../fonts/" + $file;
$filepath: "/fonts/" + $file;
@font-face {
font-family: "#{$style-name}";
@@ -29,7 +29,7 @@
}
@mixin font-face-variable($style-name, $file, $unicode-range) {
$filepath: "../fonts/" + $file;
$filepath: "/fonts/" + $file;
@font-face {
font-family: "#{$style-name}";

View File

@@ -110,9 +110,12 @@
(defn- normalize-uri
[uri-str]
;; Ensure that the path always ends with "/"; this ensures that
;; all path join operations works as expected.
(u/ensure-path-slash uri-str))
(let [uri (u/uri uri-str)]
;; Ensure that the path always ends with "/"; this ensures that
;; all path join operations works as expected.
(cond-> uri
(not (str/ends-with? (:path uri) "/"))
(update :path #(str % "/")))))
(def public-uri
(normalize-uri (or (obj/get global "penpotPublicURI")

View File

@@ -12,7 +12,6 @@
[app.common.data.macros :as dm]
[app.common.logging :as log]
[app.common.types.text :as txt]
[app.common.uri :as u]
[app.config :as cf]
[app.util.dom :as dom]
[app.util.globals :as globals]
@@ -20,6 +19,7 @@
[app.util.object :as obj]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[lambdaisland.uri :as u]
[okulary.core :as l]
[promesa.core :as p]))
@@ -138,13 +138,13 @@
"&display=block")]
(dm/str
(-> cf/public-uri
(u/join "internal/gfonts/css")
(assoc :path "/internal/gfonts/css")
(assoc :query query)))))
(defn- process-gfont-css
[css]
(let [base (u/join cf/public-uri "internal/gfonts/font")]
(str/replace css "https://fonts.gstatic.com/s" (dm/str base))))
(let [base (dm/str (assoc cf/public-uri :path "/internal/gfonts/font"))]
(str/replace css "https://fonts.gstatic.com/s" base)))
(defn- fetch-gfont-css
[url]
@@ -178,9 +178,7 @@
(defn- asset-id->uri
[asset-id]
(-> cf/public-uri
(u/join "assets/by-id/" asset-id)
(str)))
(str (u/join cf/public-uri "assets/by-id/" asset-id)))
(defn generate-custom-font-variant-css
[family variant]
@@ -372,7 +370,7 @@
:else
(let [{:keys [weight style suffix]} (get-variant font font-variant-id)
suffix (or suffix font-variant-id)
params {:uri (str (u/join cf/public-uri (str "fonts/" family "-" suffix ".woff")))
params {:uri (dm/str cf/public-uri "fonts/" family "-" suffix ".woff")
:family family
:style style
:weight weight}]

View File

@@ -14,7 +14,6 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.logging :as log]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.util.dom :as dom]
@@ -27,9 +26,7 @@
(defonce instance nil)
(defonce msgbus (rx/subject))
(defonce origin
(-> cf/rasterizer-uri
(u/join "rasterizer.html")
(dm/str)))
(dm/str (assoc cf/rasterizer-uri :path "/rasterizer.html")))
(declare send-message!)
@@ -132,9 +129,7 @@
(dom/append-child! js/document.body iframe)
(set! instance iframe))
(let [new-origin (-> cf/public-uri
(u/join "rasterizer.html")
(dm/str))]
(let [new-origin (dm/str (assoc cf/public-uri :path "/rasterizer.html"))]
(log/warn :hint "fallback to main domain" :origin new-origin)
(dom/set-attribute! iframe "src" new-origin)

View File

@@ -32,6 +32,27 @@
(def ^:private menu-icon
(deprecated-icon/icon-xref :menu (stl/css :menu-icon)))
(defn- on-restore-project
[project]
(let [on-accept #(st/emit! (dd/restore-project-immediately project))]
(st/emit! (modal/show
{:type :confirm
:title (tr "dashboard.restore-project-confirmation.title")
:message (tr "dashboard.restore-project-confirmation.description" (:name project))
:accept-style :primary
:accept-label (tr "labels.continue")
:on-accept on-accept}))))
(defn- on-delete-project
[project]
(let [accept-fn #(st/emit! (dd/delete-project-immediately project))]
(st/emit! (modal/show
{:type :confirm
:title (tr "dashboard.delete-forever-confirmation.title")
:message (tr "dashboard.delete-project-forever-confirmation.description" (:name project))
:accept-label (tr "dashboard.delete-forever-confirmation.title")
:on-accept accept-fn}))))
(mf/defc header*
{::mf/props :obj
::mf/private true}
@@ -40,7 +61,8 @@
[:div#dashboard-deleted-title {:class (stl/css :dashboard-title)}
[:h1 (tr "dashboard.projects-title")]]])
(mf/defc deleted-project-menu*
(mf/defc project-context-menu*
{::mf/private true}
[{:keys [project show on-close top left]}]
(let [top (d/nilv top 0)
left (d/nilv left 0)
@@ -48,25 +70,13 @@
on-restore-project
(mf/use-fn
(mf/deps project)
(fn []
(let [on-accept #(st/emit! (dd/restore-project-immediately project))]
(st/emit! (modal/show {:type :confirm
:title (tr "dashboard.restore-project-confirmation.title")
:message (tr "dashboard.restore-project-confirmation.description" (:name project))
:accept-style :primary
:accept-label (tr "labels.continue")
:on-accept on-accept})))))
(partial on-restore-project project))
on-delete-project
(mf/use-fn
(mf/deps project)
(fn []
(let [accept-fn #(st/emit! (dd/delete-project-immediately project))]
(st/emit! (modal/show {:type :confirm
:title (tr "dashboard.delete-forever-confirmation.title")
:message (tr "dashboard.delete-project-forever-confirmation.description" (:name project))
:accept-label (tr "dashboard.delete-forever-confirmation.title")
:on-accept accept-fn})))))
(partial on-delete-project project))
options
(mf/with-memo [on-restore-project on-delete-project]
[{:name (tr "dashboard.restore-project-button")
@@ -151,7 +161,7 @@
menu-icon]]
(when (:menu-open @local)
[:> deleted-project-menu*
[:> project-context-menu*
{:project project
:show (:menu-open @local)
:left (+ 24 (:x (:menu-pos @local)))
@@ -174,8 +184,8 @@
:limit limit
:selected-files selected-files}])]]))
(mf/defc menu*
{::mf/private true}
[{:keys [team-id section]}]
(let [on-recent-click
(mf/use-fn
@@ -222,7 +232,8 @@
(some #(= (:id project) (:project-id %))
(vals deleted-map)))))
(sort-by :modified-at)
(reverse)))
(reverse)
(not-empty)))
team-id
(get team :id)
@@ -273,37 +284,44 @@
[:*
[:> header* {:team team}]
[:section {:class (stl/css :dashboard-container :no-bg)}
[:section {:class (stl/css :dashboard-container :no-bg)
:data-testid "deleted-page-section"}
[:*
[:div {:class (stl/css :no-bg)}
[:> menu* {:team-id team-id :section :dashboard-deleted}]
[:div {:class (stl/css :deleted-info-content)}
[:p {:class (stl/css :deleted-info)}
(tr "dashboard.trash-info-text-part1")
[:span {:class (stl/css :info-text-highlight)}
(tr "dashboard.trash-info-text-part2" deletion-days)]
(tr "dashboard.trash-info-text-part3")
[:br]
(tr "dashboard.trash-info-text-part4")]
[:div {:class (stl/css :deleted-options)}
[:> button* {:variant "ghost"
:type "button"
:on-click on-restore-all}
(tr "dashboard.restore-all-deleted-button")]
[:> button* {:variant "destructive"
:type "button"
:icon "delete"
:on-click on-delete-all}
(tr "dashboard.clear-trash-button")]]]
(if (seq projects)
[:*
[:div {:class (stl/css :deleted-info-content)}
[:p {:class (stl/css :deleted-info)}
(tr "dashboard.trash-info-text-part1")
[:span {:class (stl/css :info-text-highlight)}
(tr "dashboard.trash-info-text-part2" deletion-days)]
(tr "dashboard.trash-info-text-part3")
[:br]
(tr "dashboard.trash-info-text-part4")]
[:div {:class (stl/css :deleted-options)}
[:> button* {:variant "ghost"
:type "button"
:on-click on-restore-all}
(tr "dashboard.restore-all-deleted-button")]
[:> button* {:variant "destructive"
:type "button"
:icon "delete"
:on-click on-delete-all}
(tr "dashboard.clear-trash-button")]]]
(when (seq projects)
(for [{:keys [id] :as project} projects]
(let [files (when deleted-map
(->> (vals deleted-map)
(filterv #(= id (:project-id %)))
(sort-by :modified-at #(compare %2 %1))))]
[:> deleted-project-item* {:project project
:files files
:key id}])))]]]]))
(for [{:keys [id] :as project} projects]
(let [files (when deleted-map
(->> (vals deleted-map)
(filterv #(= id (:project-id %)))
(sort-by :modified-at #(compare %2 %1))))]
[:> deleted-project-item* {:project project
:files files
:key id}]))]
;; when no deleted projects
[:div {:class (stl/css :deleted-info-content)}
[:p {:class (stl/css :deleted-info)}
(tr "dashboard.deleted.empty-state-description")]])]]]]))

View File

@@ -6,6 +6,7 @@
(ns app.main.ui.dashboard.file-menu
(:require
[app.common.data :as d]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd]
[app.main.data.event :as-alias ev]
@@ -89,12 +90,12 @@
on-duplicate
(fn [_]
(apply st/emit! (map dd/duplicate-file files))
(st/emit! (ntf/success (tr "dashboard.success-duplicate-file" (i18n/c (count files))))))
(st/emit! (ntf/success (tr "dashboard.success-duplicate-file" (i18n/c file-count)))))
on-delete-accept
(fn [_]
(apply st/emit! (map dd/delete-file files))
(st/emit! (ntf/success (tr "dashboard.success-delete-file" (i18n/c (count files))))
(st/emit! (ntf/success (tr "dashboard.success-delete-file" (i18n/c file-count)))
(dd/clear-selected-files)))
on-delete
@@ -193,7 +194,7 @@
(fn [_]
(st/emit! (dd/restore-files-immediately
(with-meta {:team-id (:id current-team)
:ids #{(:id file)}}
:ids (into #{} d/xf:map-id files)}
{:on-success #(st/emit! (ntf/success (tr "dashboard.restore-success-notification" (:name file)))
(dd/fetch-projects (:id current-team))
(dd/fetch-deleted-files (:id current-team)))
@@ -201,6 +202,7 @@
on-restore-immediately
(fn []
(prn files)
(st/emit!
(modal/show {:type :confirm
:title (tr "dashboard-restore-file-confirmation.title")
@@ -213,7 +215,7 @@
(fn []
(let [accept-fn #(st/emit! (dd/delete-files-immediately
{:team-id (:id current-team)
:ids #{(:id file)}}))]
:ids (into #{} d/xf:map-id files)}))]
(st/emit!
(modal/show {:type :confirm
:title (tr "dashboard.delete-forever-confirmation.title")
@@ -260,14 +262,12 @@
options
(if can-restore
[(when can-restore
{:name (tr "dashboard.restore-file-button")
:id "restore-file"
:handler on-restore-immediately})
(when can-restore
{:name (tr "dashboard.delete-file-button")
:id "delete-file"
:handler on-delete-immediately})]
[{:name (tr "dashboard.file-menu.restore-files-option" (i18n/c file-count))
:id "restore-file"
:handler on-restore-immediately}
{:name (tr "dashboard.file-menu.delete-files-permanently-option" (i18n/c file-count))
:id "delete-file"
:handler on-delete-immediately}]
(if multi?
[(when can-edit
{:name (tr "dashboard.duplicate-multi" file-count)

View File

@@ -7,6 +7,7 @@
(ns app.main.ui.workspace.tokens.management.forms.controls.input
(:require
[app.common.data :as d]
[app.common.files.tokens :as cft]
[app.common.types.tokens-lib :as ctob]
[app.main.data.style-dictionary :as sd]
[app.main.data.workspace.tokens.format :as dwtf]
@@ -292,7 +293,7 @@
(mf/spread-props props {:hint-formated true})
props)]
(mf/with-effect [resolve-stream tokens token input-name]
(mf/with-effect [resolve-stream tokens token input-name name]
(let [subs (->> resolve-stream
(rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token))
@@ -317,11 +318,21 @@
(reset! hint* {:message error' :type "error"}))
:else
(let [message (tr "workspace.tokens.resolved-value" (dwtf/format-token-value value))
input-value (get-in @form [:data :value input-name] "")]
(let [input-value (get-in @form [:data :value input-name] "")
resolved-value (if (= name :line-height)
(when-let [{:keys [unit value]} (cft/parse-token-value input-value)]
(let [font-size (get-in @form [:data :value :font-size] "")
calculated (case unit
"%" (/ (d/parse-double value) 100)
"px" (/ (d/parse-double value) (d/parse-double font-size))
nil value
nil)]
(dwtf/format-token-value calculated)))
(dwtf/format-token-value value))
message (tr "workspace.tokens.resolved-value" (or resolved-value value))]
(swap! form update :errors dissoc :value)
(swap! form update :extra-errors dissoc :value)
(if (= input-value (str value))
(if (= input-value (str resolved-value))
(reset! hint* {})
(reset! hint* {:message message :type "hint"})))))))]
(fn []

View File

@@ -93,9 +93,9 @@
line-height-sub-token
(mf/with-memo [token]
(if-let [value (get token :value)]
{:type :number
{:type :dimensions
:value (get value :line-height)}
{:type :number}))
{:type :dimensions}))
text-case-sub-token
(mf/with-memo [token]

View File

@@ -8477,11 +8477,15 @@ msgstr "Restore All"
msgid "dashboard.clear-trash-button"
msgstr "Clear trash"
msgid "dashboard.restore-file-button"
msgstr "Restore file"
msgid "dashboard.file-menu.restore-files-option"
msgid_plural "dashboard.file-menu.restore-files-option"
msgstr[0] "Restore file"
msgstr[1] "Restore files"
msgid "dashboard.delete-file-button"
msgstr "Delete file"
msgid "dashboard.file-menu.delete-files-permanently-option"
msgid_plural "dashboard.file-menu.delete-files-permanently-option"
msgstr[0] "Delete file"
msgstr[1] "Delete files"
msgid "dashboard.restore-project-button"
msgstr "Restore project"
@@ -8572,3 +8576,6 @@ msgstr "Restore unexpectedly slow"
msgid "dashboard.progress-notification.slow-delete"
msgstr "Deletion unexpectedly slow"
msgid "dashboard.deleted.empty-state-description"
msgstr "Your trash is empty. Deleted files and projects will appear here."

View File

@@ -8330,11 +8330,15 @@ msgstr "Restaurar todo"
msgid "dashboard.clear-trash-button"
msgstr "Vaciar papelera"
msgid "dashboard.restore-file-button"
msgstr "Restaurar archivo"
msgid "dashboard.file-menu.restore-files-option"
msgid_plural "dashboard.file-menu.restore-files-option"
msgstr[0] "Restaurar archivo"
msgstr[1] "Restaurar archivos"
msgid "dashboard.delete-file-button"
msgstr "Eliminar archivo"
msgid "dashboard.file-menu.delete-files-permanently-option"
msgid_plural "dashboard.file-menu.delete-files-permanently-option"
msgstr[0] "Eliminar archivo"
msgstr[1] "Eliminar archivos"
msgid "dashboard.restore-project-button"
msgstr "Restaurar proyecto"
@@ -8419,3 +8423,6 @@ msgstr "Restauración inesperadamente lenta"
msgid "dashboard.progress-notification.slow-delete"
msgstr "Eliminación inesperadamente lenta"
msgid "dashboard.deleted.empty-state-description"
msgstr "Tu papelera está vacía. Los archivos y proyectos eliminados aparecerán aquí."