diff --git a/frontend/resources/styles/common/refactor/common-dashboard.scss b/frontend/resources/styles/common/refactor/common-dashboard.scss index 7e1830ab7..5404d5630 100644 --- a/frontend/resources/styles/common/refactor/common-dashboard.scss +++ b/frontend/resources/styles/common/refactor/common-dashboard.scss @@ -4,12 +4,13 @@ // // Copyright (c) KALEIDOS INC -@use "common/refactor/common-refactor" as *; +@use "refactor/common-refactor" as *; .dashboard-header { align-items: center; display: flex; height: $s-64; + max-height: $s-64; justify-content: space-between; padding: $s-4 $s-16 $s-4 $s-8; position: relative; @@ -18,6 +19,7 @@ &.team { display: grid; grid-template-columns: 20% 1fr 20%; + max-height: $s-64; } .element-name { diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index eb1116b7f..bd18e57fe 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -370,6 +370,7 @@ --pill-foreground-color: var(--color-foreground-primary); --tag-background-color: var(--color-accent-primary); + --tag-background-color-disabled: var(--color-foreground-primary); --link-foreground-color: var(--color-accent-primary); @@ -377,7 +378,9 @@ --resize-area-border-color: var(--color-background-quaternary); --profile-section-background-color: var(--color-background-tertiary); - + --dashboard-list-background-color: var(--color-background-tertiary); + --dashboard-list-foreground-color: var(--color-foreground-primary); + --dashboard-list-text-foreground-color: var(--color-foreground-secondary); --flow-tag-background-color: var(--color-background-tertiary); --flow-tag-foreground-color: var(--color-foreground-secondary); --flow-tag-background-color-hover: var(--color-background-quaternary); diff --git a/frontend/src/app/main/ui/components/forms.scss b/frontend/src/app/main/ui/components/forms.scss index c9e09ec19..d77fce23b 100644 --- a/frontend/src/app/main/ui/components/forms.scss +++ b/frontend/src/app/main/ui/components/forms.scss @@ -336,7 +336,7 @@ color: var(--alert-text-foreground-color-error); } .icon svg { - stroke: var(--alert-icon-foreground-color-error); + stroke: var(--alert-text-foreground-color-error); } } } diff --git a/frontend/src/app/main/ui/dashboard.scss b/frontend/src/app/main/ui/dashboard.scss index fdf2d0d69..064694ec9 100644 --- a/frontend/src/app/main/ui/dashboard.scss +++ b/frontend/src/app/main/ui/dashboard.scss @@ -20,8 +20,8 @@ } .dashboard-content { - display: flex; - flex-direction: column; + display: grid; + grid-template-rows: $s-64 1fr; position: relative; grid-row: 1 / span 2; padding: $s-16 $s-16 0 0; diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index cbbbb421e..a2f80d058 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -24,6 +24,7 @@ [app.main.ui.dashboard.change-owner] [app.main.ui.dashboard.team-form] [app.main.ui.icons :as i] + [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] @@ -31,6 +32,31 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) + +(def ^:private arrow-icon + (i/icon-xref :arrow-refactor (stl/css :arrow-icon))) + +(def ^:private menu-icon + (i/icon-xref :menu-refactor (stl/css :menu-icon))) + +(def ^:private warning-icon + (i/icon-xref :msg-warning-refactor (stl/css :warning-icon))) + +(def ^:private success-icon + (i/icon-xref :msg-success-refactor (stl/css :success-icon))) + +(def ^:private image-icon + (i/icon-xref :img-refactor (stl/css :image-icon))) + +(def ^:private user-icon + (i/icon-xref :user-refactor (stl/css :user-icon))) + +(def ^:private document-icon + (i/icon-xref :document-refactor (stl/css :document-icon))) + +(def ^:private group-icon + (i/icon-xref :group-refactor (stl/css :group-icon))) + (mf/defc header {::mf/wrap [mf/memo] ::mf/wrap-props false} @@ -163,17 +189,16 @@ (tr "modals.invite-team-member.title")] (when-not (= "" @error-text) - [:div {:class (stl/css :error-msg)} - [:span {:class (stl/css :icon)} i/msg-error] - [:span {:class (stl/css :message)} @error-text]]) + [:& context-notification {:content @error-text + :type :error}]) (when (some current-data-emails current-members-emails) - [:div {:class (stl/css :warning-msg)} - [:span {:class (stl/css :icon)} i/msg-warning] - [:span {:class (stl/css :message)} (tr "modals.invite-member.repeated-invitation")]]) + [:& context-notification {:content (tr "modals.invite-member.repeated-invitation") + :type :warning}]) [:div {:class (stl/css :role-select)} - [:p {:class (stl/css :role-title)} (tr "onboarding.choice.team-up.roles")] + [:p {:class (stl/css :role-title)} + (tr "onboarding.choice.team-up.roles")] [:& fm/select {:name :role :options roles}]] [:div {:class (stl/css :invitation-row)} @@ -190,6 +215,7 @@ [:div {:class (stl/css :action-buttons)} [:> fm/submit-button* {:label (tr "modals.invite-member-confirm.accept") + :class (stl/css :accept-btn) :disabled (and (boolean (some current-data-emails current-members-emails)) (empty? (remove current-members-emails current-data-emails)))}]]]])) @@ -202,8 +228,8 @@ [{:keys [member profile]}] (let [is-you? (= (:id profile) (:id member))] [:* - [:div {:class (stl/css :member-image)} - [:img {:src (cfg/resolve-profile-photo-url member)}]] + [:img {:class (stl/css :member-image) + :src (cfg/resolve-profile-photo-url member)}] [:div {:class (stl/css :member-info)} [:div {:class (stl/css :member-name)} (:name member) (when is-you? @@ -238,19 +264,25 @@ [:div {:class (stl/css :rol-selector :has-priv) :on-click on-show} [:span {:class (stl/css :rol-label)} (tr role)] - [:span {:class (stl/css :icon)} i/arrow-down]] + arrow-icon] [:div {:class (stl/css :rol-selector)} [:span {:class (stl/css :rol-label)} (tr role)]]) [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :options-dropdown)} - [:li {:on-click on-set-admin} (tr "labels.admin")] - [:li {:on-click on-set-editor} (tr "labels.editor")] - ;; Temporarily disabled viewer role - ;; https://tree.taiga.io/project/penpot/issue/1083 - ;; [:li {:on-click set-viewer} (tr "labels.viewer")] + [:ul {:class (stl/css :roles-dropdown)} + [:li {:on-click on-set-admin + :class (stl/css :rol-dropdown-item)} + (tr "labels.admin")] + [:li {:on-click on-set-editor + :class (stl/css :rol-dropdown-item)} + (tr "labels.editor")] + ;; Temporarily disabled viewer role + ;; https://tree.taiga.io/project/penpot/issue/1083 + ;; [:li {:on-click set-viewer} (tr "labels.viewer")] (when you-owner? - [:li {:on-click (partial on-set-owner member)} (tr "labels.owner")])]]])) + [:li {:on-click (partial on-set-owner member) + :class (:stl/css :rol-dropdown-item)} + (tr "labels.owner")])]]])) (mf/defc member-actions {::mf/wrap-props false} @@ -267,15 +299,20 @@ [:* (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]]) + [:button {:class (stl/css :menu-btn) + :on-click on-show} + menu-icon]) [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} + [:ul {:class (stl/css :actions-dropdown)} (when is-you? - [:li {:on-click on-leave} (tr "dashboard.leave-team")]) + [:li {:on-click on-leave + :class (stl/css :action-dropdown-item) + :key "is-you-option"} (tr "dashboard.leave-team")]) (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) - [:li {:on-click on-delete} (tr "labels.remove-member")])]]])) + [:li {:on-click on-delete + :class (stl/css :action-dropdown-item) + :key "is-not-you-option"} (tr "labels.remove-member")])]]])) (defn- set-role! [member-id role] (let [params {:member-id member-id :role role}] @@ -396,10 +433,10 @@ :else on-leave)] [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :name)} + [:div {:class (stl/css :table-field :field-name)} [:& member-info {:member member :profile profile}]] - [:div {:class (stl/css :table-field :roles)} + [:div {:class (stl/css :table-field :field-roles)} [:& rol-info {:member member :team team :on-set-admin on-set-admin @@ -407,7 +444,7 @@ :on-set-owner on-set-owner :profile profile}]] - [:div {:class (stl/css :table-field :actions)} + [:div {:class (stl/css :table-field :field-actions)} [:& member-actions {:member member :profile profile :team team @@ -427,8 +464,8 @@ [:div {:class (stl/css :dashboard-table :team-members)} [:div {:class (stl/css :table-header)} - [:div {:class (stl/css :table-field :name)} (tr "labels.member")] - [:div {:class (stl/css :table-field :role)} (tr "labels.role")]] + [:div {:class (stl/css :table-field :title-field-name)} (tr "labels.member")] + [:div {:class (stl/css :table-field :title-field-role)} (tr "labels.role")]] [:div {:class (stl/css :table-rows)} [:& team-member @@ -499,14 +536,20 @@ [:div {:class (stl/css :rol-selector :has-priv) :on-click on-show} [:span {:class (stl/css :rol-label)} label] - [:span {:class (stl/css :icon)} i/arrow-down]] + arrow-icon] [:div {:class (stl/css :rol-selector)} [:span {:class (stl/css :rol-label)} label]]) [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :options-dropdown)} - [:li {:data-role "admin" :on-click on-change'} (tr "labels.admin")] - [:li {:data-role "editor" :on-click on-change'} (tr "labels.editor")]]]])) + [:ul {:class (stl/css :roles-dropdown)} + [:li {:data-role "admin" + :class (stl/css :rol-dropdown-item) + :on-click on-change'} + (tr "labels.admin")] + [:li {:data-role "editor" + :class (stl/css :rol-dropdown-item) + :on-click on-change'} + (tr "labels.editor")]]]])) (mf/defc invitation-status-badge {::mf/wrap-props false} @@ -514,8 +557,8 @@ [:div {:class (stl/css-case :status-badge true - :expired (= status :expired) - :pending (= status :pending))} + :badge-expired (= status :expired) + :badge-pending (= status :pending))} [:span {:class (stl/css :status-label)} (if (= status :expired) (tr "labels.expired-invitation") @@ -600,13 +643,21 @@ on-show (mf/use-fn #(reset! show? true))] [:* - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]] + [:button {:class (stl/css :menu-btn) + :on-click on-show} + menu-icon] + [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} - [:li {:on-click on-copy} (tr "labels.copy-invitation-link")] - [:li {:on-click on-resend} (tr "labels.resend-invitation")] - [:li {:on-click on-delete} (tr "labels.delete-invitation")]]]])) + [:ul {:class (stl/css :actions-dropdown :invitations-dropdown)} + [:li {:on-click on-copy + :class (stl/css :action-dropdown-item)} + (tr "labels.copy-invitation-link")] + [:li {:on-click on-resend + :class (stl/css :action-dropdown-item)} + (tr "labels.resend-invitation")] + [:li {:on-click on-delete + :class (stl/css :action-dropdown-item)} + (tr "labels.delete-invitation")]]]])) (mf/defc invitation-row {::mf/wrap [mf/memo] @@ -626,20 +677,20 @@ mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] (st/emit! (dd/update-team-invitation-role (with-meta params mdata))))))] - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :mail)} email] + [:div {:class (stl/css :table-row :table-row-invitations)} + [:div {:class (stl/css :table-field :field-email)} email] - [:div {:class (stl/css :table-field :roles)} + [:div {:class (stl/css :table-field :field-roles)} [:& invitation-role-selector {:can-invite? can-invite? :role role :status status :on-change on-change-role}]] - [:div {:class (stl/css :table-field :status)} + [:div {:class (stl/css :table-field :field-status)} [:& invitation-status-badge {:status status}]] - [:div {:class (stl/css :table-field :actions)} + [:div {:class (stl/css :table-field :field-actions)} (when can-invite? [:& invitation-actions {:invitation invitation @@ -660,11 +711,11 @@ can-invite? (or owner? admin?) team-id (:id team)] - [:div {:class (stl/css :dashboard-table :invitations)} + [:div {:class (stl/css :invitations)} [:div {:class (stl/css :table-header)} - [:div {:class (stl/css :table-field :name)} (tr "labels.invitations")] - [:div {:class (stl/css :table-field :role)} (tr "labels.role")] - [:div {:class (stl/css :table-field :status)} (tr "labels.status")]] + [:div {:class (stl/css :title-field-name)} (tr "labels.invitations")] + [:div {:class (stl/css :title-field-role)} (tr "labels.role")] + [:div {:class (stl/css :title-field-status)} (tr "labels.status")]] (if (empty? invitations) [:& empty-invitation-table {:can-invite? can-invite?}] [:div {:class (stl/css :table-rows)} @@ -692,7 +743,7 @@ [:* [:& header {:section :dashboard-team-invitations :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-invitations)} + [:section {:class (stl/css :dashboard-team-invitations)} ;; TODO: We should consider adding a "loading state" here ;; with an (if (nil? invitations) [:& loading-state] [:& invitations]) (when-not (nil? invitations) @@ -828,20 +879,17 @@ (tr "modals.edit-webhook.submit-label") (tr "modals.create-webhook.submit-label"))}]]]]]])) - (mf/defc webhooks-hero {::mf/wrap-props false} [] [:div {:class (stl/css :webhooks-hero-container)} - [:div {:class (stl/css :webhooks-hero)} - [:div {:class (stl/css :desc)} - [:h2 (tr "labels.webhooks")] - [:& i18n/tr-html {:label "dashboard.webhooks.description"}]] - - [:div - {:class (stl/css :btn-primary) - :on-click #(st/emit! (modal/show :webhook {}))} - [:span (tr "dashboard.webhooks.create")]]]]) + [:h2 {:class (stl/css :hero-title)} + (tr "labels.webhooks")] + [:& i18n/tr-html {:class (stl/css :hero-desc) + :label "dashboard.webhooks.description"}] + [:button {:class (stl/css :hero-btn) + :on-click #(st/emit! (modal/show :webhook {}))} + (tr "dashboard.webhooks.create")]]) (mf/defc webhook-actions {::mf/wrap-props false} @@ -852,23 +900,24 @@ [:* - [:span {:class (stl/css :icon) - :on-click on-show} [i/actions]] + [:button {:class (stl/css :menu-btn) + :on-click on-show} + menu-icon] [:& dropdown {:show @show? :on-close on-hide} - [:ul {:class (stl/css :dropdown :actions-dropdown)} - [:li {:on-click on-edit} (tr "labels.edit")] - [:li {:on-click on-delete} (tr "labels.delete")]]]])) + [:ul {:class (stl/css :webhook-actions-dropdown)} + [:li {:on-click on-edit + :class (stl/css :webhook-dropdown-item)} (tr "labels.edit")] + [:li {:on-click on-delete + :class (stl/css :webhook-dropdown-item)} (tr "labels.delete")]]]])) (mf/defc last-delivery-icon {::mf/wrap-props false} [{:keys [success? text]}] - [:div {:class (stl/css :last-delivery-icon)} - [:div {:class (stl/css :tooltip)} - [:div {:class (stl/css :label)} text] - [:div {:class (stl/css :arrow-down)}]] + [:div {:class (stl/css :last-delivery-icon) + :title text} (if success? - [:span {:class (stl/css :icon :success)} i/msg-success] - [:span {:class (stl/css :icon :failure)} i/msg-warning])]) + success-icon + warning-icon)]) (mf/defc webhook-item {::mf/wrap [mf/memo]} @@ -913,12 +962,12 @@ (dm/str " " (tr "errors.webhooks.unexpected-status" (extract-status error-code))))))] - [:div {:class (stl/css :table-row)} - [:div {:class (stl/css :table-field :last-delivery)} - [:div {:class (stl/css :icon-container)} - [:& last-delivery-icon - {:success? (nil? error-code) - :text last-delivery-text}]]] + [:div {:class (stl/css :table-row :webhook-row)} + [:div {:class (stl/css :table-field :last-delivery) + :title last-delivery-text} + (if (nil? error-code) + success-icon + warning-icon)] [:div {:class (stl/css :table-field :uri)} [:div (dm/str (:uri webhook))]] [:div {:class (stl/css :table-field :active)} @@ -933,10 +982,9 @@ (mf/defc webhooks-list {::mf/wrap-props false} [{:keys [webhooks]}] - [:div {:class (stl/css :dashboard-table)} - [:div {:class (stl/css :table-rows)} - (for [webhook webhooks] - [:& webhook-item {:webhook webhook :key (:id webhook)}])]]) + [:div {:class (stl/css :table-rows :webhook-table)} + (for [webhook webhooks] + [:& webhook-item {:webhook webhook :key (:id webhook)}])]) (mf/defc team-webhooks-page {::mf/wrap-props false} @@ -1005,38 +1053,51 @@ [:* [:& header {:section :dashboard-team-settings :team team}] - [:section {:class (stl/css :dashboard-container :dashboard-team-settings)} - [:div {:class (stl/css :team-settings)} - [:div {:class (stl/css :horizontal-blocks)} - [:div {:class (stl/css :block :info-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-info")] - [:div {:class (stl/css :name)} (:name team)] - [:div {:class (stl/css :icon)} - (when can-edit? - [:span {:class (stl/css :update-overlay) - :on-click on-image-click} i/image]) - [:img {:src (cfg/resolve-team-photo-url team)}] - (when can-edit? - [:& file-uploader {:accept "image/jpeg,image/png" - :multi false - :ref finput - :on-selected on-file-selected}])]] + [:section {:class (stl/css :dashboard-team-settings)} + [:div {:class (stl/css :block :info-block)} + [:div {:class (stl/css :block-label)} + (tr "dashboard.team-info")] + [:div {:class (stl/css :block-text)} + (:name team)] + [:div {:class (stl/css :team-icon)} + (when can-edit? + [:button {:class (stl/css :update-overlay) + :on-click on-image-click} + image-icon]) + [:img {:class (stl/css :team-image) + :src (cfg/resolve-team-photo-url team)}] + (when can-edit? + [:& file-uploader {:accept "image/jpeg,image/png" + :multi false + :ref finput + :on-selected on-file-selected}])]] - [:div {:class (stl/css :block :owner-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-members")] - [:div {:class (stl/css :owner)} - [:span {:class (stl/css :icon)} [:img {:src (cfg/resolve-profile-photo-url owner)}]] - [:span {:class (stl/css :text)} (str (:name owner) " (" (tr "labels.owner") ")")]] - [:div {:class (stl/css :summary)} - [:span {:class (stl/css :icon)} i/user] - [:span {:class (stl/css :text)} (tr "dashboard.num-of-members" (count members-map))]]] + [:div {:class (stl/css :block)} + [:div {:class (stl/css :block-label)} + (tr "dashboard.team-members")] - [:div {:class (stl/css :block :stats-block)} - [:div {:class (stl/css :label)} (tr "dashboard.team-projects")] - [:div {:class (stl/css :projects)} - [:span {:class (stl/css :icon)} i/folder] - [:span {:class (stl/css :text)} (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] - [:div {:class (stl/css :files)} - [:span {:class (stl/css :icon)} i/file-html] - [:span {:class (stl/css :text)} (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]]]])) + [:div {:class (stl/css :block-content)} + [:img {:class (stl/css :owner-icon) + :src (cfg/resolve-profile-photo-url owner)}] + [:span {:class (stl/css :block-text)} + (str (:name owner) " (" (tr "labels.owner") ")")]] + + [:div {:class (stl/css :block-content)} + user-icon + [:span {:class (stl/css :block-text)} + (tr "dashboard.num-of-members" (count members-map))]]] + + [:div {:class (stl/css :block)} + [:div {:class (stl/css :block-label)} + (tr "dashboard.team-projects")] + + [:div {:class (stl/css :block-content)} + group-icon + [:span {:class (stl/css :block-text)} + (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] + + [:div {:class (stl/css :block-content)} + document-icon + [:span {:class (stl/css :block-text)} + (tr "labels.num-of-files" (i18n/c (:files stats)))]]]]])) diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index 2f1b8840e..c5d67a027 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -7,577 +7,446 @@ @use "common/refactor/common-refactor.scss" as *; @use "common/refactor/common-dashboard"; -.dashboard-container { - flex: 1 0 0; - margin-right: $s-16; - overflow-y: auto; +// Dashboard team settings +.dashboard-team-settings { + display: grid; + grid-template-rows: auto auto 1fr; + justify-items: center; + gap: $s-24; width: 100%; - border-top: $s-1 solid $db-quaternary; - - &.dashboard-projects { - user-select: none; - } - &.dashboard-shared { - width: calc(100vw - $s-320); - margin-right: $s-52; - } - - &.search { - margin-top: $s-12; - } + border-top: $s-1 solid var(--panel-border-color); + overflow-y: auto; } -.dashboard-team-webhooks { - display: flex; - flex-direction: column; - width: $s-800; - margin-left: $s-120; - margin-top: $s-80; - border: none; - align-items: flex-start; -} - -.webhooks-empty { - align-items: center; - background-color: transparent; - border-radius: $br-8; - border: $s-1 solid $db-quaternary; - color: $df-secondary; - display: flex; - flex-direction: column; - font-size: $fs-12; - justify-content: center; - margin-top: $s-32; +.block { + display: grid; + grid-auto-rows: min-content; + gap: $s-8; max-width: $s-1000; - min-height: $s-136; - padding: $s-32; - text-align: center; - width: $s-468; + width: $s-1000; +} + +.info-block { + position: relative; + padding-top: $s-180; +} + +.block-label { + @include headlineSmallTypography; + color: var(--title-foreground-color); +} + +.block-text { + color: var(--title-foreground-color-hover); +} + +.block-content { + display: grid; + grid-template-columns: $s-32 1fr; + align-items: center; + gap: $s-12; +} + +.owner-icon { + width: $s-32; + height: $s-32; + border-radius: 50%; +} + +.user-icon, +.document-icon, +.group-icon { + @extend .button-icon; + margin: 0 auto; + stroke: var(--icon-foreground); +} + +.team-icon { + --update-button-opacity: 0; + position: absolute; + top: 0; + left: 0; + height: $s-120; + width: $s-120; + padding: $s-16; + + &:hover { + --update-button-opacity: 1; + } +} + +.team-image { + border-radius: 50%; + width: $s-120; + height: $s-120; +} + +.update-overlay { + opacity: var(--update-button-opacity); + @include buttonStyle; + @include flexCenter; + position: absolute; + top: $s-16; + left: $s-16; + height: 100%; + width: 100%; + z-index: $z-index-modal; + border-radius: $br-circle; + background-color: $da-primary; +} + +.image-icon { + @extend .button-icon; + min-width: $s-24; + min-height: $s-24; + stroke: var(--icon-foreground-hover); +} + +// TEAM MEMBERS PAGE +.dashboard-team-members { + display: grid; + justify-items: center; + width: 100%; + height: 100%; + padding-top: $s-20; + border-top: $s-1 solid var(--panel-border-color); + overflow-y: auto; + scrollbar-gutter: stable; +} + +.team-members { + display: grid; + grid-template-rows: auto 1fr; + height: fit-content; + max-width: $s-1000; + width: $s-1000; +} + +.table-header { + @include headlineSmallTypography; + display: grid; + align-items: center; + grid-template-columns: 43% 1fr $s-108 $s-12; + height: $s-40; + width: 100%; + max-width: $s-1000; + padding: 0 $s-16; + user-select: none; + color: var(--title-foreground-color); +} + +.table-rows { + display: grid; + grid-auto-rows: $s-64; + gap: $s-16; + width: 100%; + height: 100%; + max-width: $s-1000; + margin-top: $s-16; + color: var(--title-foreground-color); +} + +.table-row { + display: grid; + grid-template-columns: 43% 1fr auto; + align-items: center; + height: $s-64; + width: 100%; + padding: 0 $s-16; + border-radius: $br-8; + background-color: var(--dashboard-list-background-color); + color: var(--dashboard-list-foreground-color); +} + +.title-field-name { + width: 43%; + min-width: $s-300; +} + +.title-field-roles { + position: relative; + cursor: default; +} + +.field-name { + display: grid; + grid-template-columns: auto 1fr; + gap: $s-16; + width: 43%; + min-width: $s-300; +} + +.field-roles { + position: relative; + cursor: default; +} + +.field-actions { + position: relative; +} + +// MEMBER INFO +.member-image { + height: $s-32; + width: $s-32; + border-radius: $br-circle; +} + +.member-info { + display: grid; + grid-template-rows: 1fr 1fr; + width: 100%; +} + +.member-name, +.member-email { + @include textEllipsis; + @include bodyLargeTypography; +} + +.member-email { + @include bodySmallTypography; + color: var(--dashboard-list-text-foreground-color); +} + +.you { + color: var(--dashboard-list-text-foreground-color); + margin-left: $s-6; +} + +// ROL INFO +.rol-selector { + position: relative; + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + height: $s-32; + min-width: $s-160; + width: fit-content; + padding: $s-4 $s-8; + border-radius: $br-8; + border-color: var(--menu-background-color-hover); + background-color: var(--menu-background-color-hover); + font-size: $fs-14; +} + +.has-priv { + cursor: pointer; +} + +.rol-label { + user-select: none; +} + +.roles-dropdown { + @extend .menu-dropdown; + bottom: calc(-1 * $s-76); + width: fit-content; + min-width: $s-160; +} + +.rol-dropdown-item { + @extend .menu-item-base; +} + +// MEMBER ACTIONS +.menu-icon { + @extend .button-icon; + stroke: var(--icon-foreground); +} + +.menu-btn { + @include buttonStyle; +} + +.actions-dropdown { + @extend .menu-dropdown; + bottom: calc(-1 * $s-32); + right: 0; + left: unset; + width: fit-content; + min-width: $s-160; +} + +.action-dropdown-item { + @extend .menu-item-base; +} + +// TEAM INVITATION PAGE +.dashboard-team-invitations { + display: grid; + justify-items: center; + width: 100%; + height: 100%; + padding-top: $s-20; + border-top: $s-1 solid var(--panel-border-color); + overflow-y: auto; + scrollbar-gutter: stable; +} + +.invitations { + display: grid; + grid-template-rows: auto 1fr; + height: fit-content; + max-width: $s-1000; + width: $s-1000; +} + +.table-row-invitations { + grid-template-columns: 43% 1fr $s-108 $s-12; + align-items: center; +} + +.empty-invitations { + display: grid; + place-items: center; + align-content: center; + height: $s-156; + max-width: $s-1000; + width: 100%; + margin-top: $s-16; + border: $s-1 solid var(--panel-border-color); + border-radius: $br-8; + color: var(--dashboard-list-text-foreground-color); +} + +.title-field-status { + position: relative; + cursor: default; +} + +.field-email { + @include textEllipsis; + @include bodyLargeTypography; + display: grid; + align-items: center; +} + +// STATUS BADGE +.status-badge { + @include flexCenter; + @include headlineSmallTypography; + min-width: $s-76; + width: fit-content; + height: $s-24; + border-radius: $br-8; + color: var(--pill-foreground-color); +} + +.badge-pending { + background-color: var(--status-widget-background-color-warning); +} + +.badge-expired { + background-color: var(--tag-background-color-disabled); +} + +.invitations-dropdown { + bottom: calc(-1 * $s-112); + right: calc(-1 * $s-20); +} + +// WEBHOOKS SECTION +.dashboard-team-webhooks { + display: grid; + grid-template-rows: auto 1fr; + justify-items: center; + gap: $s-24; + width: 100%; + height: 100%; + padding-top: $s-16; + border-top: $s-1 solid var(--panel-border-color); + overflow-y: auto; } .webhooks-hero-container { + display: grid; + gap: $s-32; + max-width: $s-1000; + width: $s-1000; +} + +.webhooks-empty { + display: grid; + place-items: center; + align-content: center; + height: $s-156; max-width: $s-1000; width: 100%; - display: flex; - flex-direction: column; - - width: $s-468; - background-color: transparent; - - .upload-button { - width: $s-100; - } - - .btn-secondary { - margin-left: $s-12; - } + padding: $s-32; + border: $s-1 solid var(--panel-border-color); + border-radius: $br-8; + color: var(--dashboard-list-text-foreground-color); } .webhooks-hero { font-size: $fs-14; - display: flex; - flex-direction: column; + display: grid; + grid-template-rows: auto 1fr auto; gap: $s-32; - justify-content: space-between; margin-top: $s-32; margin: 0; padding: $s-32; padding: 0; width: $s-468; - - .desc { - color: $df-secondary; - width: 100%; - - h2 { - color: $df-primary; - font-size: $fs-24; - font-weight: regular; - margin-bottom: $s-32; - } - p { - color: $df-secondary; - margin-bottom: 0; - font-size: $fs-16; - } - } - .btn-primary { - @extend .button-primary; - height: $s-32; - } } -.dropdown { - background-color: $db-tertiary; - border-radius: $br-8; - border: $s-1 solid $db-quaternary; - box-shadow: 0 $s-2 $s-8 rgba(0, 0, 0, 0.25); - left: calc(-1 * $s-144); - max-height: $s-480; - min-width: $s-252; - overflow-y: auto; - position: absolute; - top: $s-32; - width: $s-152; - z-index: $z-index-4; - - hr { - margin: 0; - border-color: $df-secondary; - } - - li { - display: flex; - align-items: center; - cursor: pointer; - font-size: $fs-14; - padding: $s-4 $s-16; - border-radius: $br-8; - height: $s-40; - margin: $s-6; - color: $df-primary; - - &:hover { - background-color: $db-quaternary; - } - - &.title { - font-weight: $fw700; - cursor: default; - } - } - - .separator { - border-color: transparent; - margin-top: $s-8; - } - - &.options-dropdown { - li { - color: $df-primary; - &.warning { - color: var(--element-foreground-warning); - } - } - } +.hero-title { + @include bigTitleTipography; + color: var(--dashboard-list-foreground-color); } -.dashboard-table { - display: flex; - flex-direction: column; - align-items: center; - margin-top: $s-20; - font-size: $fs-16; - - .table-header { - color: $df-secondary; - display: grid; - font-size: $fs-12; - grid-template-columns: 43% 1fr $s-108 $s-12; - height: $s-40; - max-width: $s-1000; - padding: 0 $s-16; - text-transform: uppercase; - user-select: none; - width: 100%; - } - .table-rows { - display: flex; - flex-direction: column; - max-width: $s-1000; - width: 100%; - margin-top: $s-16; - color: $db-secondary; - } - - .table-row { - align-items: center; - background-color: $db-tertiary; - border-radius: $br-8; - color: $df-primary; - display: flex; - height: $s-64; - padding: 0 $s-16; - width: 100%; - - &:not(:first-child) { - margin-top: $s-16; - } - } - - .table-field { - display: flex; - align-items: center; - - .icon { - padding-left: $s-12; - cursor: pointer; - } - - &.name { - width: 43%; - min-width: $s-300; - display: flex; - } - &.roles { - flex-grow: 1; - cursor: default; - position: relative; - .rol-label { - user-select: none; - } - .rol-selector { - &.has-priv { - cursor: pointer; - } - min-width: $s-160; - height: $s-32; - display: flex; - justify-content: space-between; - align-items: center; - padding: $s-4 $s-8; - font-size: $fs-14; - background-color: $db-quaternary; - border-color: transparent; - border-radius: $br-8; - } - - .dropdown { - left: 0; - } - } - - &.actions { - position: relative; - .actions-dropdown { - max-height: $s-480; - min-width: $s-180; - } - - svg { - fill: $df-secondary; - } - } - - &.status { - .status-badge { - min-width: $s-76; - height: $s-24; - display: flex; - justify-content: center; - align-items: center; - border-radius: $br-8; - color: $db-primary; - text-transform: uppercase; - - &.pending { - background-color: var(--status-color-warning-500); - } - - &.expired { - background-color: $df-secondary; - } - - .status-label { - font-size: $fs-12; - } - } - } - - &.uri { - flex-grow: 1; - } - - &.active { - min-width: $s-100; - } - - &.last-delivery { - display: flex; - justify-content: center; - width: $s-52; - position: relative; - .success svg { - fill: $da-tertiary; - width: $s-16; - height: $s-16; - } - .failure svg { - fill: var(--element-foreground-warning); - width: $s-16; - height: $s-16; - } - - .icon-container { - width: $s-16; - height: $s-16; - overflow-x: visible; - } - - .icon { - padding: 0; - } - } - - .tooltip { - display: none; - position: absolute; - top: calc(-1 * $s-56); - left: 50%; - transform: translate(-50%, 0); - text-align: center; - - .label { - border-radius: $br-4; - color: $df-primary; - background-color: $db-secondary; - white-space: nowrap; - padding: $s-12 $s-20; - } - - .arrow-down { - margin: 0 auto; - width: 0; - height: 0; - border-left: $s-8 solid transparent; - border-right: $s-8 solid transparent; - border-top: $s-8 solid $db-secondary; - } - } - - .last-delivery-icon:hover { - .tooltip { - display: block; - } - } - } - - .member-info { - display: flex; - flex-direction: column; - margin-left: $s-16; - } - .member-name { - font-size: $fs-16; - } - .you { - color: $df-secondary; - margin-left: $s-6; - } - .member-email { - color: $df-secondary; - font-size: $fs-12; - } - .member-image { - height: $s-32; - width: $s-32; - img { - border-radius: 50%; - } - } - - .dashboard-team-webhooks & { - width: $s-800; - .table-rows { - padding-top: 0; - .table-row { - font-size: $fs-16; - min-height: $s-40; - height: fit-content; - .name { - color: $df-primary; - max-width: $s-480; - } - .expiration-date { - color: $df-secondary; - } - } - } - } - - &.team-members { - margin-bottom: $s-52; - } - - &.invitations { - .table-row { - display: grid; - grid-template-columns: 43% 1fr $s-108 $s-12; - } - } - - svg { - width: $s-12; - height: $s-12; - fill: $df-secondary; - } -} - -.empty-invitations { - height: $s-156; - max-width: $s-1000; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - margin-top: $s-16; - border: $s-1 solid $db-quaternary; - border-radius: $br-8; +.hero-desc { color: $df-secondary; + margin-bottom: 0; + font-size: $fs-16; + max-width: $s-512; } -.team-settings { - display: flex; - justify-content: center; - margin-top: $s-16; - - svg { - width: $s-20; - height: $s-20; - } - - .horizontal-blocks { - display: flex; - max-width: $s-1000; - justify-content: space-between; - width: 100%; - flex-direction: column; - gap: $s-24; - } - - .block { - display: flex; - max-width: $s-324; - width: $s-324; - flex-direction: column; - padding: $s-12; - - .label { - color: $df-secondary; - font-size: $fs-12; - text-transform: uppercase; - } - .icon svg { - fill: $df-secondary; - } - .name, - .text { - color: $df-primary; - } - } - - .info-block { - position: relative; - padding-top: $s-180; - - .name { - margin-top: $s-12; - font-size: $fs-24; - color: $df-primary; - @include text-ellipsis; - margin-right: $s-88; - } - - .icon { - position: absolute; - padding: $s-16; - right: 0; - top: 0; - left: 0; - height: $s-120; - width: $s-120; - - img { - border-radius: 50%; - width: $s-120; - height: $s-120; - } - - .update-overlay { - opacity: 0; - cursor: pointer; - position: absolute; - display: flex; - justify-content: center; - align-items: center; - border-radius: 50%; - color: $df-primary; - z-index: $z-index-modal; - background-color: $da-primary; - height: 100%; - width: 100%; - - svg { - fill: $db-primary; - } - } - - &:hover { - .update-overlay { - opacity: 1; - top: $s-16; - left: $s-16; - } - } - } - } - - .owner-block { - img { - width: $s-32; - height: $s-32; - border-radius: 50%; - } - - svg { - width: $s-12; - height: $s-12; - fill: $db-secondary; - } - - .owner { - margin-top: $s-6; - display: flex; - align-items: center; - color: $db-secondary; - .icon { - margin-right: $s-12; - } - } - - .summary { - margin-top: $s-6; - color: $db-secondary; - .icon { - padding: 0 $s-12; - margin-right: $s-12; - } - } - } - - .stats-block { - svg { - fill: $db-secondary; - } - - .projects, - .files { - margin-top: $s-8; - display: flex; - align-items: center; - color: $db-secondary; - - .icon { - display: flex; - align-items: center; - padding: 0 $s-2; - margin-right: $s-12; - } - } - } +.hero-btn { + @extend .button-primary; + height: $s-32; + max-width: $s-512; } +.webhook-table { + height: fit-content; +} + +.webhook-row { + display: grid; + align-items: center; + grid-template-columns: auto 1fr auto auto; + gap: $s-16; +} + +.actions { + position: relative; +} + +.webhook-actions-dropdown { + @extend .menu-dropdown; + right: calc(-1 * $s-16); + bottom: calc(-1 * $s-40); + width: fit-content; + min-width: $s-160; +} + +.webhook-dropdown-item { + @extend .menu-item-base; +} + +.success-icon { + @extend .button-icon; + stroke: var(--alert-icon-foreground-color-success); +} + +.warning-icon { + @extend .button-icon; + stroke: var(--alert-icon-foreground-color-warning); +} + +// INVITE MEMBERS MODAL .modal-team-container { @extend .modal-container-base; @include menuShadow; @@ -592,83 +461,46 @@ top: $s-216; right: $s-32; } - .modal-title { - @include headlineMediumTypography; - height: $s-32; - color: var(--modal-title-foreground-color); - } - .error-msg { - @include flexRow; - height: $s-32; - border-radius: $br-8; - background-color: var(--alert-background-color-error); - .icon { - @include flexCenter; - width: $s-16; - height: $s-24; - svg { - @extend .button-icon; - stroke: var(--alert-icon-foreground-color-error); - } - } - .message { - @include bodySmallTypography; - color: var(--alert-foreground-color-error); - } - } - - .warning-msg { - @include flexRow; - height: $s-32; - border-radius: $br-8; - background-color: var(--alert-background-color-warning); - padding: $s-24 $s-8; - gap: $s-8; - margin-bottom: $s-16; - - .icon { - @include flexCenter; - width: $s-16; - height: $s-24; - svg { - @extend .button-icon; - stroke: var(--alert-icon-foreground-color-warning); - } - } - .message { - @include bodySmallTypography; - color: var(--alert-foreground-color-warning); - } - } - - .role-select { - @include flexColumn; - row-gap: $s-8; - .role-title { - @include bodyLargeTypography; - margin: 0; - color: var(--modal-title-foreground-color); - } - } - - .invitation-row { - margin-top: $s-8; - margin-bottom: $s-24; - } - - .action-buttons { - display: flex; - justify-content: flex-end; - button { - @extend .modal-accept-btn; - &:disabled { - @extend .button-disabled; - } - } - } } -// WEBHOOKS +.modal-title { + @include headlineMediumTypography; + height: $s-32; + color: var(--modal-title-foreground-color); +} + +.role-select { + @include flexColumn; + row-gap: $s-8; +} + +.arrow-icon { + @extend .button-icon; + stroke: var(--icon-foreground); + transform: rotate(90deg); +} + +.role-title { + @include bodyLargeTypography; + margin: 0; + color: var(--modal-title-foreground-color); +} + +.invitation-row { + margin-top: $s-8; + margin-bottom: $s-24; +} + +.action-buttons { + display: flex; + justify-content: flex-end; +} + +.accept-btn { + @extend .modal-accept-btn; +} + +// WEBHOOKS MODAL .modal-overlay { @extend .modal-overlay-base; diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index acb581697..39575d601 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -177,10 +177,12 @@ {::mf/wrap-props false} [props] (let [label (obj/get props "label") + class (obj/get props "class") tag-name (obj/get props "tag-name" "p") params (obj/get props "params" []) html (apply tr (d/concat-vec [label] params))] - [:> tag-name {:dangerouslySetInnerHTML #js {:__html html}}])) + [:> tag-name {:dangerouslySetInnerHTML #js {:__html html} + :className class}])) ;; DEPRECATED (defn use-locale