diff --git a/backend/src/app/rpc/mutations/profile.clj b/backend/src/app/rpc/mutations/profile.clj index 906ec83ad..12b8e9d64 100644 --- a/backend/src/app/rpc/mutations/profile.clj +++ b/backend/src/app/rpc/mutations/profile.clj @@ -16,7 +16,6 @@ [app.loggers.audit :as audit] [app.media :as media] [app.metrics :as mtx] - [app.rpc.mutations.projects :as projects] [app.rpc.mutations.teams :as teams] [app.rpc.queries.profile :as profile] [app.setup.initial-data :as sid] @@ -256,28 +255,15 @@ :code :email-already-exists :cause e))))))) - (defn create-profile-relations [conn profile] - (let [team (teams/create-team conn {:profile-id (:id profile) - :name "Default" - :is-default true}) - project (projects/create-project conn {:profile-id (:id profile) - :team-id (:id team) - :name "Drafts" - :is-default true}) - params {:team-id (:id team) - :profile-id (:id profile) - :project-id (:id project) - :role :owner}] - - (teams/create-team-role conn params) - (projects/create-project-role conn params) - + (let [team (teams/create-team conn {:profile-id (:id profile) + :name "Default" + :is-default true})] (-> profile (profile/strip-private-attrs) (assoc :default-team-id (:id team)) - (assoc :default-project-id (:id project))))) + (assoc :default-project-id (:default-project-id team))))) ;; --- MUTATION: Login diff --git a/backend/src/app/rpc/mutations/teams.clj b/backend/src/app/rpc/mutations/teams.clj index 625a13af6..0f44afb17 100644 --- a/backend/src/app/rpc/mutations/teams.clj +++ b/backend/src/app/rpc/mutations/teams.clj @@ -32,6 +32,7 @@ ;; --- Mutation: Create Team (declare create-team) +(declare create-team-entry) (declare create-team-role) (declare create-team-default-project) @@ -42,15 +43,21 @@ (sv/defmethod ::create-team [{:keys [pool] :as cfg} params] (db/with-atomic [conn pool] - (let [team (create-team conn params) - params (assoc params - :team-id (:id team) - :role :owner)] - (create-team-role conn params) - (create-team-default-project conn params) - team))) + (create-team conn params))) (defn create-team + "This is a complete team creation process, it creates the team + object and all related objects (default role and default project)." + [conn params] + (let [team (create-team-entry conn params) + params (assoc params + :team-id (:id team) + :role :owner) + project (create-team-default-project conn params)] + (create-team-role conn params) + (assoc team :default-project-id (:id project)))) + +(defn- create-team-entry [conn {:keys [id name is-default] :as params}] (let [id (or id (uuid/next)) is-default (if (boolean? is-default) is-default false)] @@ -59,23 +66,24 @@ :name name :is-default is-default}))) -(defn create-team-role +(defn- create-team-role [conn {:keys [team-id profile-id role] :as params}] (let [params {:team-id team-id :profile-id profile-id}] (->> (perms/assign-role-flags params role) (db/insert! conn :team-profile-rel)))) -(defn create-team-default-project +(defn- create-team-default-project [conn {:keys [team-id profile-id] :as params}] (let [project {:id (uuid/next) :team-id team-id :name "Drafts" - :is-default true}] - (projects/create-project conn project) + :is-default true} + project (projects/create-project conn project)] (projects/create-project-role conn {:project-id (:id project) :profile-id profile-id - :role :owner}))) + :role :owner}) + project)) ;; --- Mutation: Update Team @@ -293,28 +301,18 @@ ;; --- Mutation: Invite Member +(declare create-team-invitation) + (s/def ::email ::us/email) (s/def ::invite-team-member (s/keys :req-un [::profile-id ::team-id ::email ::role])) (sv/defmethod ::invite-team-member - [{:keys [pool tokens] :as cfg} {:keys [profile-id team-id email role] :as params}] + [{:keys [pool] :as cfg} {:keys [profile-id team-id email role] :as params}] (db/with-atomic [conn pool] (let [perms (teams/get-permissions conn profile-id team-id) profile (db/get-by-id conn :profile profile-id) - member (profile/retrieve-profile-data-by-email conn email) - team (db/get-by-id conn :team team-id) - itoken (tokens :generate - {:iss :team-invitation - :exp (dt/in-future "48h") - :profile-id (:id profile) - :role role - :team-id team-id - :member-email (:email member email) - :member-id (:id member)}) - ptoken (tokens :generate-predefined - {:iss :profile-identity - :profile-id (:id profile)})] + team (db/get-by-id conn :team team-id)] (when-not (:is-admin perms) (ex/raise :type :validation @@ -326,24 +324,71 @@ :code :profile-is-muted :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) - (when (and member (not (eml/allow-send-emails? conn member))) - (ex/raise :type :validation - :code :member-is-muted - :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) - - ;; Secondly check if the invited member email is part of the - ;; global spam/bounce report. - (when (eml/has-bounce-reports? conn email) - (ex/raise :type :validation - :code :email-has-permanent-bounces - :hint "looks like the email you invite has been repeatedly reported as spam or permanent bounce")) - - (eml/send! {::eml/conn conn - ::eml/factory eml/invite-to-team - :public-uri (:public-uri cfg) - :to email - :invited-by (:fullname profile) - :team (:name team) - :token itoken - :extra-data ptoken}) + (create-team-invitation + (assoc cfg + :email email + :conn conn + :team team + :profile profile + :role role)) nil))) + +(defn- create-team-invitation + [{:keys [conn tokens team profile role email] :as cfg}] + (let [member (profile/retrieve-profile-data-by-email conn email) + itoken (tokens :generate + {:iss :team-invitation + :exp (dt/in-future "48h") + :profile-id (:id profile) + :role role + :team-id (:id team) + :member-email (:email member email) + :member-id (:id member)}) + ptoken (tokens :generate-predefined + {:iss :profile-identity + :profile-id (:id profile)})] + + (when (and member (not (eml/allow-send-emails? conn member))) + (ex/raise :type :validation + :code :member-is-muted + :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) + + ;; Secondly check if the invited member email is part of the + ;; global spam/bounce report. + (when (eml/has-bounce-reports? conn email) + (ex/raise :type :validation + :code :email-has-permanent-bounces + :hint "looks like the email you invite has been repeatedly reported as spam or permanent bounce")) + + (eml/send! {::eml/conn conn + ::eml/factory eml/invite-to-team + :public-uri (:public-uri cfg) + :to email + :invited-by (:fullname profile) + :team (:name team) + :token itoken + :extra-data ptoken}))) + + +;; --- Mutation: Create Team & Invite Members + +(s/def ::emails ::us/set-of-emails) +(s/def ::create-team-and-invite-members + (s/and ::create-team (s/keys :req-un [::emails ::role]))) + +(sv/defmethod ::create-team-and-invite-members + [{:keys [pool] :as cfg} {:keys [profile-id emails role] :as params}] + (db/with-atomic [conn pool] + (let [team (create-team conn params) + profile (db/get-by-id conn :profile profile-id)] + + ;; Create invitations for all provided emails. + (doseq [email emails] + (create-team-invitation + (assoc cfg + :conn conn + :team team + :profile profile + :email email + :role role))) + team))) diff --git a/backend/src/app/rpc/queries/projects.clj b/backend/src/app/rpc/queries/projects.clj index 47886784a..1e92869e4 100644 --- a/backend/src/app/rpc/queries/projects.clj +++ b/backend/src/app/rpc/queries/projects.clj @@ -79,12 +79,14 @@ where f.project_id = p.id and deleted_at is null) as count from project as p + inner join team as t on (t.id = p.team_id) left join team_project_profile_rel as tpp on (tpp.project_id = p.id and tpp.team_id = p.team_id and tpp.profile_id = ?) where p.team_id = ? and p.deleted_at is null + and t.deleted_at is null order by p.modified_at desc") (defn retrieve-projects @@ -108,26 +110,26 @@ (def sql:all-projects "select p1.*, t.name as team_name, t.is_default as is_default_team from project as p1 - inner join team as t - on t.id = p1.team_id + inner join team as t on (t.id = p1.team_id) where t.id in (select team_id from team_profile_rel as tpr where tpr.profile_id = ? and (tpr.can_edit = true or tpr.is_owner = true or tpr.is_admin = true)) + and t.deleted_at is null and p1.deleted_at is null union select p2.*, t.name as team_name, t.is_default as is_default_team from project as p2 - inner join team as t - on t.id = p2.team_id + inner join team as t on (t.id = p2.team_id) where p2.id in (select project_id from project_profile_rel as ppr where ppr.profile_id = ? and (ppr.can_edit = true or ppr.is_owner = true or ppr.is_admin = true)) + and t.deleted_at is null and p2.deleted_at is null order by team_name, name;") diff --git a/backend/test/app/services_projects_test.clj b/backend/test/app/services_projects_test.clj index 5f23577f3..a59991e38 100644 --- a/backend/test/app/services_projects_test.clj +++ b/backend/test/app/services_projects_test.clj @@ -43,7 +43,7 @@ (t/is (nil? (:error out))) (let [result (:result out)] - (t/is (= 1 (count result))) + (t/is (= 2 (count result))) (t/is project-id (get-in result [0 :id])) (t/is (= "test project" (get-in result [0 :name]))))) @@ -55,15 +55,15 @@ (t/is (nil? (:error out))) (let [result (:result out)] - (t/is (= 2 (count result))) + (t/is (= 3 (count result))) (t/is (not= project-id (get-in result [0 :id]))) (t/is (= "Drafts" (get-in result [0 :name]))) (t/is (= "Default" (get-in result [0 :team-name]))) (t/is (= true (get-in result [0 :is-default-team]))) - (t/is project-id (get-in result [1 :id])) - (t/is (= "test project" (get-in result [1 :name]))) - (t/is (= "team1" (get-in result [1 :team-name]))) - (t/is (= false (get-in result [1 :is-default-team]))))) + (t/is project-id (get-in result [2 :id])) + (t/is (= "test project" (get-in result [2 :name]))) + (t/is (= "team1" (get-in result [2 :team-name]))) + (t/is (= false (get-in result [2 :is-default-team]))))) ;; rename project (let [data {::th/type :rename-project @@ -95,7 +95,7 @@ (t/is (nil? (:error out))) (t/is (nil? (:result out)))) - ;; query a list of projects after delete" + ;; query a list of projects after delete (let [data {::th/type :projects :team-id (:id team) :profile-id (:id profile)} @@ -103,7 +103,7 @@ ;; (th/print-result! out) (t/is (nil? (:error out))) (let [result (:result out)] - (t/is (= 0 (count result))))) + (t/is (= 1 (count result))))) )) (t/deftest permissions-checks-create-project diff --git a/backend/test/app/services_teams_test.clj b/backend/test/app/services_teams_test.clj index 6dd7470da..6e2aaeea7 100644 --- a/backend/test/app/services_teams_test.clj +++ b/backend/test/app/services_teams_test.clj @@ -130,7 +130,7 @@ (let [result (task {:max-age (dt/duration {:minutes 1})})] (t/is (nil? result))) - ;; query the list of projects of a after hard deletion + ;; query the list of projects after hard deletion (let [data {::th/type :projects :team-id (:id team) :profile-id (:id profile1)} diff --git a/backend/test/app/test_helpers.clj b/backend/test/app/test_helpers.clj index 43334b883..f503e5d66 100644 --- a/backend/test/app/test_helpers.clj +++ b/backend/test/app/test_helpers.clj @@ -126,7 +126,8 @@ :password "123123" :is-demo false} params)] - (->> (#'profile/create-profile conn params) + (->> params + (#'profile/create-profile conn) (#'profile/create-profile-relations conn))))) (defn create-project* @@ -159,15 +160,10 @@ ([i params] (create-team* *pool* i params)) ([conn i {:keys [profile-id] :as params}] (us/assert uuid? profile-id) - (let [id (mk-uuid "team" i) - team (#'teams/create-team conn {:id id - :profile-id profile-id - :name (str "team" i)})] - (#'teams/create-team-role conn - {:team-id id - :profile-id profile-id - :role :owner}) - team))) + (let [id (mk-uuid "team" i)] + (teams/create-team conn {:id id + :profile-id profile-id + :name (str "team" i)})))) (defn create-file-media-object* ([params] (create-file-media-object* *pool* params)) @@ -350,3 +346,11 @@ (defn reset-mock! [m] (reset! m @(mk/make-mock {}))) + +(defn pause + [] + (let [^java.io.Console cnsl (System/console)] + (println "[waiting RETURN]") + (.readLine cnsl) + nil)) + diff --git a/frontend/resources/images/on-solo-hover.svg b/frontend/resources/images/on-solo-hover.svg new file mode 100644 index 000000000..d75553178 --- /dev/null +++ b/frontend/resources/images/on-solo-hover.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="24.47353642154394" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="10.276508697997087 -10.666460420698968 24.47353642154394 45.833972589785844" height="45.833972589785844" style="-webkit-print-color-adjust: exact;"><g id="shape-68c61310-2040-11ec-9082-1597698bcafa" width="133" height="70" fill="none"><g id="shape-68c61311-2040-11ec-9082-1597698bcafa"><path d="M34.15111509349208,-6.042576477324019C32.728545198285246,-6.330271155262835,31.92939323677365,-3.376021339875024,31.573755956590503,-2.4511143742279273C31.03134089353898,-4.792719923592813,32.95730466396617,-10.268928805500309,31.43334627304739,-10.431571776022338C29.909377991244583,-10.594250374600506,28.25067219538414,-7.913146659964696,27.49821990591863,-6.170389430908017C26.74573283532027,-4.427587759031667,25.891378375591557,-1.7644073640558418,25.713212854031553,-2.2272258928269366C25.535081233357232,-2.6900095615483224,27.15263561710435,-6.757552580227184,25.941143250794084,-7.1652527066157745C24.72968451746874,-7.572893837298125,23.954342086512952,-5.090038220419956,23.25052490786311,-3.268314742871553C21.417817942427064,1.4754114884185583,10.659070475242515,0.4358613513782075,10.659070475242515,0.4358613513782075L10.492289692115264,34.903869918353394C10.950455754343238,34.90895547471155,28.014759237585167,30.129601494621056,32.24939430527593,19.872789654186818C35.25119864070166,13.145361698193483,32.406657583041124,6.514941655593702,33.309134176787666,1.1371823352346837C33.78537439733827,-1.58885192629441,35.57368117762144,-5.754847357929975,34.15111509349208,-6.042576477324019ZZ" style="fill: rgb(255, 255, 255); fill-opacity: 1;"/></g><g id="shape-68c61312-2040-11ec-9082-1597698bcafa"><path d="M22.85523475939408,17.21695965203071C22.427431361919844,16.01479714589459,23.05111296555242,14.69085378511636,24.24829081777807,14.259819026323385C25.445434386402667,13.828783886987821,26.762725789724755,14.45390600122937,27.19052918719808,15.656068507365035C27.618298301069444,16.85823063295993,26.994616697437777,18.18217399373725,25.79747312881318,18.61320913307327C24.60029527658753,19.044243891866245,23.283003873265443,18.41912177762515,22.85523475939408,17.21695965203071ZZ" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g><g id="shape-68c61313-2040-11ec-9082-1597698bcafa"><path d="M17.110277419337763,8.332660542501344C14.855684713780647,9.317767677909615,11.627251872240777,12.863450146366631,13.47442592751213,18.054309980863763C15.321633118238424,23.24527363442894,19.819776888222805,23.267580687635473,22.247438584220617,22.768891367604738" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/on-solo.svg b/frontend/resources/images/on-solo.svg new file mode 100644 index 000000000..08d76020d --- /dev/null +++ b/frontend/resources/images/on-solo.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="24.47353642154485" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="10.276508697995268 -10.66646042069624 24.47353642154485 45.833972589782206" height="45.833972589782206" style="-webkit-print-color-adjust: exact;"><g id="shape-b0db2ad1-203d-11ec-9082-1597698bcafa" width="133" height="70" fill="none"><g id="shape-b0db2ad2-203d-11ec-9082-1597698bcafa"><path d="M34.15111509349208,-6.042576477324474C32.728545198285246,-6.3302711552623805,31.92939323677365,-3.376021339874569,31.573755956590503,-2.4511143742274726C31.03134089353898,-4.792719923592813,32.95730466396617,-10.268928805500764,31.43334627304739,-10.431571776021883C29.909377991244583,-10.59425037460096,28.25067219538414,-7.913146659964696,27.49821990591863,-6.170389430908472C26.74573283532027,-4.427587759031667,25.891378375591557,-1.7644073640558418,25.713212854031553,-2.2272258928269366C25.535081233357232,-2.6900095615483224,27.15263561710435,-6.757552580227639,25.941143250794084,-7.165252706616229C24.72968451746874,-7.57289383729767,23.954342086512952,-5.090038220419956,23.25052490786311,-3.268314742871553C21.417817942427064,1.4754114884181035,10.659070475242515,0.4358613513777527,10.659070475242515,0.4358613513777527L10.492289692115264,34.90386991835294C10.950455754343238,34.90895547471155,28.014759237585167,30.129601494621056,32.24939430527593,19.872789654186818C35.25119864070166,13.145361698193483,32.406657583041124,6.514941655594157,33.309134176787666,1.1371823352346837C33.78537439733827,-1.58885192629441,35.57368117762144,-5.754847357929975,34.15111509349208,-6.042576477324474ZZ" style="fill: rgb(250, 181, 245);"/></g><g id="shape-b0db51e0-203d-11ec-9082-1597698bcafa"><path d="M22.85523475939408,17.21695965203071C22.427431361919844,16.01479714589459,23.05111296555242,14.690853785115905,24.24829081777807,14.259819026323385C25.445434386402667,13.828783886987367,26.762725789724755,14.45390600122937,27.19052918719808,15.65606850736549C27.618298301069444,16.85823063295993,26.994616697437777,18.182173993736797,25.79747312881318,18.613209133072814C24.60029527658753,19.044243891866245,23.283003873265443,18.41912177762515,22.85523475939408,17.21695965203071ZZ" style="stroke-width: 1; stroke: white;"/></g><g id="shape-b0db51e1-203d-11ec-9082-1597698bcafa"><path d="M17.110277419337763,8.33266054250089C14.855684713780647,9.317767677909615,11.627251872240777,12.863450146366631,13.47442592751213,18.054309980863763C15.321633118238424,23.245273634429395,19.819776888222805,23.267580687635927,22.247438584220617,22.768891367604738" style="stroke-width: 1; stroke: white;"/></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/on-teamup-hover.svg b/frontend/resources/images/on-teamup-hover.svg new file mode 100644 index 000000000..c012588a0 --- /dev/null +++ b/frontend/resources/images/on-teamup-hover.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="58.97218209120183" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="0 0 58.97218209120183 34.02461765404769" height="34.02461765404769" style="-webkit-print-color-adjust: exact;"><g id="shape-56b9b850-3ba4-11ec-a51e-9dc34ea791b7"><g id="shape-0431a4b0-2044-11ec-9082-1597698bcafa" width="96" height="107" fill="none"><g id="shape-0431a4b1-2044-11ec-9082-1597698bcafa"><path d="M11.602428230001351,20.29156831594628C12.96091601317039,20.246694227447506,14.141179330516024,20.105337800590405,14.676638164904034,20.052727380546685C16.104560413273248,19.912431232226254,18.070963388380733,19.669672407356757,18.249168931040003,19.122483239904568C18.646677342781913,17.90193438961137,17.31910625621822,17.646456981843585,15.343966676359742,17.293366035031795C12.691381181413817,16.819172878339486,13.000606671054811,15.52582487655286,12.892396662949068,14.101330684896084C12.75297750712798,12.265864668423092,11.97626475040488,10.955493769995883,10.058874331964944,10.687813096056516C8.163646789748782,10.423225671403543,9.30724176959211,7.142206106756021,13.948158437434358,5.840002658534331C18.335963374422136,4.60885836276384,18.136935263438318,0.4596849111167103,18.136935263438318,0.4596849111167103L0.02253717583789694,0.5287075987273511C0.02253717583789694,0.5287075987273511,-0.20150582308633602,1.9811727180626804,1.1450954357324008,3.6178556226475393C2.4916961404151152,5.254519555615843,6.482940492022863,5.0919332480982575,6.782266936440465,7.347466732293697C6.903558382829033,9.84110475846569,5.386938093891331,11.315412538593364,3.471298117720835,11.342865107158332C2.894864812139531,11.359739921987739,2.4408166919829455,11.827095340908727,2.457125783853371,12.386778122409396C2.3485513314271884,13.394745944208807,4.045872147769842,13.099828102716401,3.977479116536415,13.540557924901123C3.8216582745344567,13.948220278861754,2.9234340028315273,13.295794177475727,2.4262748894770994,13.30179000137241C1.7737003729280332,13.320850822652574,1.2596836917246037,13.84995929679826,1.278190149450893,14.483554372966182C1.2966966071790011,15.117149449134331,1.8407191625474297,15.615335036713759,2.493293679094677,15.596274215433596C2.8382692138893617,15.590128420470364,3.74169682536467,15.0536142290091,3.778215936329616,15.320256576048678C3.0257684573107326,15.951439192579983,1.6696887978805535,16.07941768073647,1.6673134217244296,17.237363230855635C1.6927807600714004,18.10861518528486,2.4408558940349394,18.793648510604044,3.3382132606611776,18.76744171005521C3.9803081790641954,18.748662299556827,4.551440953341626,18.36452453900256,4.795581229288473,17.78722781114743C4.923075422148941,18.072004542010973,5.133965692955826,18.429043399179818,5.486484599065989,18.85442951049299C6.57926929862424,20.17316437001682,9.338281885930883,20.366357135430917,11.602428230001351,20.29156831594628ZZ" style="fill: rgb(255, 255, 255);"/></g><g id="shape-0431a4b2-2044-11ec-9082-1597698bcafa"><path d="M7.71249313745011,16.384882897213856C8.22060129817055,16.264887295582184,8.730376864878963,16.57487873194259,8.851122116918305,17.077262923656008C8.971867368957646,17.579647115369426,8.657863708510376,18.084172518387504,8.14977477508819,18.204167558415747C7.641685841664184,18.324162598443763,7.13189160179445,18.01419069530357,7.011127122455036,17.51180706519358C6.890381870415695,17.009422873480162,7.204404204027014,16.504877937241872,7.71249313745011,16.384882897213856ZZ" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g><g id="shape-0431a4b3-2044-11ec-9082-1597698bcafa"><path d="M11.624898516201029,14.358535148349802C11.356573396985368,13.501041460592432,10.191539442935209,12.188607512236104,8.21790163886817,12.653619115011224C6.2442259343361,13.11865081260953,6.052639608603386,14.750722925934042,6.136035752132557,15.651802609503648" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g></g><g id="shape-0431a4b4-2044-11ec-9082-1597698bcafa" width="115" height="120" fill="none"><g id="shape-0431a4b5-2044-11ec-9082-1597698bcafa"><path d="M11.862488256155302,33.441762024086756C11.862488256155302,33.441762024086756,12.720858484573,32.75523040950952,13.934561097475125,32.52559708434819C15.14800775439835,32.29595331943665,16.28633969733619,32.51987715549126,17.55573219400594,31.442430937011977C19.25077445484203,30.306307060000336,19.604498278121355,29.67029873345541,21.744686454311704,29.410589098655237C21.748427728015486,29.411412302940562,21.752607803230603,29.413107299721332,21.756242686972655,29.41353853720034C24.536115188827353,29.286490195458327,25.423494767207558,30.106207873591757,27.339726143598455,28.95439352250378C29.776074386952132,27.38317423583021,30.87042639316769,24.645090640941135,30.87042639316769,24.645090640941135C30.87042639316769,24.645090640941135,32.560869444104355,25.415939659260175,33.42132107923226,23.882396651011277C34.37427833597485,22.183996371967396,32.65875281433273,21.112831953185605,32.65875281433273,21.112831953185605C32.65875281433273,21.112831953185605,33.75616983587224,19.040996281273465,33.396495907524695,16.937653855708504C33.03679352785821,14.834310888904838,31.49985713935621,9.867588190290235,33.96985902130564,7.519240112441139C36.439831392599444,5.170947179330824,39.62406153370557,6.287013782444774,41.543234103571194,4.946328040665321C43.462404769560635,3.6055029973408637,43.10938507854917,1.3084298230205604,43.056145811086026,0.8380928838082582L20.94546201295543,0.3940772500964158C20.94546201295543,0.3940772500964158,20.083533287267528,2.8610597900371886,21.385402509808955,6.13728112342551C22.953890361618505,10.084290547725914,27.122847892115715,8.282234895293414,27.539225812382938,10.844449001662952C27.96722529724866,13.477993064751672,23.125505664977936,11.685597013264214,20.99561748290398,14.889877975621175C19.6776711685352,17.514245513251808,19.350913140147895,20.199304066899458,19.496309586241296,22.535516245647386C19.083635969230272,22.45218400806266,18.229497598652415,22.316306527785628,17.081936918716565,22.80193108755907C15.748075041366064,23.36640094347831,16.066707484655126,24.100764348625034,14.9172182933753,23.66739603781093C13.19808501235002,23.019278902559563,12.69832182250957,23.287383239375913,12.69832182250957,23.287383239375913L12.989709489634151,24.741759304450397C12.989709489634151,24.741759304450397,14.029680175024623,24.8773003599847,15.198522096507077,25.147650743244185C16.801622665480863,25.696743293718782,17.123839654824224,23.983503215360088,18.401647446283278,24.140865480622324C19.679454708075355,24.29825558887387,19.843586707033865,25.037673709259252,19.509293705707023,24.999366873871622C16.826892835212675,25.05958379255094,16.326642922236715,26.304820107565547,14.796612269686193,26.86090671624038C13.24430109359946,27.33743891638369,13.311327268943387,27.000856295873973,12.100544143645493,27.032136633992423C10.889761548011847,27.063389129121788,10.17180504587941,27.759733686253185,10.17180504587941,27.759733686253185L10.60825193497567,29.38368127040735C12.207860084073218,28.16838281422679,13.977830309117962,28.710037765045854,14.95019719211723,28.552726882758634C17.05577014361097,28.22899302717792,18.06022364709588,26.432861115284823,19.87928913081032,26.982051198388262C17.174564387548344,28.35790077846491,17.615691336552118,29.394711217003987,16.401060728817356,30.180318637148275C15.573401214586738,30.69027254339153,14.352679667236771,30.9701919638037,13.402935042723584,31.072394184775703C12.453218710633337,31.174605299883524,11.007620191902788,31.924403466527338,11.007620191902788,31.924403466527338L11.862488256155302,33.441762024086756ZZ" style="fill: rgb(255, 255, 255);"/></g><g id="shape-0431a4b6-2044-11ec-9082-1597698bcafa"><path d="M26.367916328855245,21.541864617551028C25.752024052401794,21.084638509604474,24.874335589574002,21.203753489894325,24.407570785847383,21.807892178728707C23.940805982120764,22.412030867562862,24.061708216020634,23.272455921100345,24.677600492474085,23.729682029047126C25.293520690586774,24.18693652122238,26.17118070209017,24.067820999693367,26.637945505817697,23.463682310858985C27.104710839211293,22.859515779035064,26.983808605310514,21.99909072549758,26.367916328855245,21.541864617551028ZZ" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g><g id="shape-0431a4b7-2044-11ec-9082-1597698bcafa"><path d="M22.290717374943597,16.541050556458003C23.171689612728187,15.546130552714885,25.54200073357333,14.462977670809778,27.935050108376345,16.23759190460669C30.328155856161175,18.012235063871458,29.602746949542052,20.315197630067587,28.945962813089864,21.47640372124374" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g></g><g id="shape-0431a4b8-2044-11ec-9082-1597698bcafa" width="101" height="114" fill="none"><g id="shape-0431a4b9-2044-11ec-9082-1597698bcafa"><path d="M37.367513280914864,20.133292488739926C37.367513280914864,20.133292488739926,34.47324632323489,22.306539800594464,34.84863363869681,25.35643818049425C35.22402695335677,28.406336462319132,41.879424902604114,29.29405617621819,46.87282537913234,28.083008645274504C51.86624767451849,26.871984813334848,53.88083298476067,30.625859726817225,53.88083298476067,30.625859726817225L53.83623996239203,23.916641636305712C53.83623996239203,23.916641636305712,54.7814403921011,26.233739014496905,56.22537117411866,26.940924119363217C57.66919482759204,27.648110975533427,58.90788052120024,27.48766036815573,58.90788052120024,27.48766036815573L58.62188616189724,9.114836810030056C58.62188616189724,9.114836810030056,54.14155875509368,8.71759425925643,52.30578058531228,11.63949281358532C50.469980989822034,14.561391718175628,51.25416169458822,15.029686005807207,50.53323418202854,14.724504968003885C49.812285601169606,14.419346143429948,50.13895137746567,8.829826936175095,50.13895137746567,8.829826936175095C50.13895137746567,8.829826936175095,47.311935842498315,11.134827867711692,42.578878758490646,12.341595327470941C37.84582203189075,13.548384650198614,30.80430857443389,16.918822766545645,37.367513280914864,20.133292488739926ZZ" style="fill: rgb(255, 255, 255);"/></g><g id="shape-0431a4ba-2044-11ec-9082-1597698bcafa"><path d="M43.78310756909923,21.898392426748387C43.71040464575435,21.16903145811534,43.07154757977605,20.6376317756974,42.356130134013256,20.711456496495202C41.6407341139593,20.785280867033862,41.11970689025384,21.43636973165053,41.19238874529856,22.165752913512506C41.265091668644345,22.89511388214646,41.903949449440915,23.426557290499886,42.6193668952028,23.352732569701175C43.33476255784717,23.278886336195,43.85578942414486,22.627775608611273,43.78310756909923,21.898392426748387ZZ" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g><g id="shape-0431cbc0-2044-11ec-9082-1597698bcafa"><path d="M44.564542765305305,15.866730564345517C45.95380390294031,15.817601817570676,48.46007021846344,16.85388775037177,48.77394038244802,20.003267049274427C49.08781090384218,23.152668211144146,46.79729976525323,24.281104032500707,45.437423948057585,24.625374981520736" style="stroke-width: 1; stroke: rgb(49, 239, 184); stroke-opacity: 1;"/></g></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/on-teamup.svg b/frontend/resources/images/on-teamup.svg new file mode 100644 index 000000000..10b85bf13 --- /dev/null +++ b/frontend/resources/images/on-teamup.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="58.97218285395502" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="0 0 58.97218285395502 34.02461708792089" height="34.02461708792089" style="-webkit-print-color-adjust: exact;"><g id="shape-eb85b840-3ba3-11ec-a51e-9dc34ea791b7"><g id="shape-b0dba000-203d-11ec-9082-1597698bcafa" width="96" height="107" fill="none"><g id="shape-b0dba001-203d-11ec-9082-1597698bcafa"><path d="M11.602428229999532,20.291568315949007C12.960916013168571,20.24669422745046,14.141179330514206,20.10533780059268,14.676638164902215,20.052727380549186C16.10456041327143,19.9124312322283,18.070963388378914,19.66967240735903,18.249168931038184,19.122483239906614C18.646677342780094,17.901934389613416,17.3191062562164,17.646456981846313,15.343966676357923,17.293366035034524C12.691381181411998,16.819172878341305,13.000606671052992,15.525824876555816,12.892396662947249,14.10133068489904C12.752977507126161,12.26586466842582,11.976264750403061,10.955493769998611,10.058874331963125,10.687813096058562C8.163646789746963,10.423225671405817,9.307241769590291,7.14220610675784,13.94815843743254,5.840002658536832C18.335963374420317,4.608858362767023,18.1369352634365,0.45968491111943877,18.1369352634365,0.45968491111943877L0.02253717583607795,0.5287075987298522C0.02253717583607795,0.5287075987298522,-0.201505823088155,1.981172718064954,1.1450954357305818,3.617855622650495C2.4916961404132962,5.254519555618572,6.482940492021044,5.091933248100759,6.782266936438646,7.347466732296198C6.903558382827214,9.841104758467736,5.386938093889512,11.31541253859632,3.471298117719016,11.342865107160833C2.894864812137712,11.359739921989785,2.4408166919811265,11.827095340911,2.457125783851552,12.38677812241167C2.3485513314253694,13.394745944211536,4.045872147768023,13.099828102718675,3.977479116534596,13.540557924903624C3.8216582745326377,13.948220278864937,2.9234340028297083,13.295794177478456,2.4262748894752804,13.301790001375593C1.7737003729262142,13.320850822655302,1.2596836917227847,13.84995929680099,1.278190149449074,14.483554372968683C1.2966966071771822,15.117149449136377,1.8407191625456107,15.615335036715805,2.493293679092858,15.596274215436097C2.8382692138875427,15.590128420472865,3.741696825362851,15.053614229012055,3.778215936327797,15.320256576051179C3.0257684573089136,15.951439192582257,1.6696887978787345,16.079417680738516,1.6673134217226107,17.237363230858136C1.6927807600695814,18.108615185287817,2.4408558940331204,18.793648510606545,3.3382132606593586,18.767441710057938C3.9803081790623764,18.748662299559328,4.551440953339807,18.364524539005288,4.795581229286654,17.787227811149933C4.923075422147122,18.07200454201393,5.133965692954007,18.429043399181865,5.48648459906417,18.85442951049572C6.579269298622421,20.17316437001955,9.338281885929064,20.366357135433645,11.602428229999532,20.291568315949007ZZ" style="fill: rgb(250, 181, 245);"/></g><g id="shape-b0dba002-203d-11ec-9082-1597698bcafa"><path d="M7.712493137448291,16.384882897215903C8.22060129816873,16.26488729558423,8.730376864877144,16.57487873194532,8.851122116916486,17.077262923658964C8.971867368955827,17.5796471153717,8.657863708508557,18.084172518390005,8.149774775086371,18.204167558417794C7.641685841662365,18.32416259844649,7.131891601792631,18.014190695305842,7.0111271224532175,17.51180706519608C6.890381870413876,17.009422873482436,7.204404204025195,16.5048779372446,7.712493137448291,16.384882897215903ZZ" style="stroke-width: 1; stroke: white;"/></g><g id="shape-b0dba003-203d-11ec-9082-1597698bcafa"><path d="M11.62489851619921,14.35853514835253C11.35657339698355,13.501041460594934,10.19153944293339,12.188607512239287,8.217901638866351,12.653619115013498C6.244225934334281,13.118650812612032,6.052639608601567,14.750722925936316,6.136035752130738,15.651802609506376" style="stroke-width: 1; stroke: white;"/></g></g><g id="shape-b0db51e2-203d-11ec-9082-1597698bcafa" width="115" height="120" fill="none"><g id="shape-b0db51e3-203d-11ec-9082-1597698bcafa"><path d="M11.862488256153483,33.44176202408926C11.862488256153483,33.44176202408926,12.720858484571181,32.75523040951248,13.934561097473306,32.52559708435092C15.14800775439653,32.29595331943892,16.28633969733437,32.519877155494214,17.555732194004122,31.44243093701425C19.25077445484021,30.306307060002837,19.604498278119536,29.670298733458367,21.744686454309885,29.41058909865751C21.748427728013667,29.411412302943063,21.752607803228784,29.41310729972338,21.756242686970836,29.41353853720284C24.536115188825534,29.28649019546083,25.42349476720574,30.10620787359403,27.339726143596636,28.954393522506507C29.776074386950313,27.383174235832485,30.87042639316587,24.645090640943636,30.87042639316587,24.645090640943636C30.87042639316587,24.645090640943636,32.560869444102536,25.41593965926313,33.42132107923044,23.88239665101355C34.37427833597303,22.183996371969442,32.65875281433091,21.112831953188106,32.65875281433091,21.112831953188106C32.65875281433091,21.112831953188106,33.75616983587042,19.04099628127642,33.396495907522876,16.937653855710778C33.03679352785639,14.834310888907567,31.49985713935439,9.867588190292736,33.96985902130382,7.519240112443185C36.439831392597625,5.17094717933378,39.62406153370375,6.287013782447502,41.543234103569375,4.94632804066805C43.462404769558816,3.6055029973431374,43.10938507854735,1.308429823023289,43.05614581108421,0.8380928838105319L20.945462012953612,0.3940772500991443C20.945462012953612,0.3940772500991443,20.08353328726571,2.8610597900396897,21.385402509807136,6.137281123427783C22.953890361616686,10.084290547728415,27.122847892113896,8.282234895295915,27.53922581238112,10.844449001665453C27.96722529724684,13.477993064753718,23.125505664976117,11.685597013266488,20.995617482902162,14.889877975623676C19.67767116853338,17.514245513254536,19.350913140146076,20.199304066902187,19.496309586239477,22.535516245649887C19.083635969228453,22.45218400806516,18.229497598650596,22.316306527788583,17.081936918714746,22.80193108756157C15.748075041364245,23.366400943480585,16.066707484653307,24.100764348627308,14.91721829337348,23.667396037812978C13.198085012348201,23.01927890256229,12.69832182250775,23.28738323937887,12.69832182250775,23.28738323937887L12.989709489632332,24.7417593044529C12.989709489632332,24.7417593044529,14.029680175022804,24.877300359987203,15.198522096505258,25.14765074324623C16.801622665479044,25.69674329372083,17.123839654822405,23.98350321536236,18.40164744628146,24.140865480624598C19.679454708073536,24.2982555888766,19.843586707032046,25.03767370926198,19.509293705705204,24.99936687387435C16.826892835210856,25.059583792552985,16.326642922234896,26.304820107568048,14.796612269684374,26.860906716242425C13.244301093597642,27.337438916386418,13.311327268941568,27.000856295876474,12.100544143643674,27.032136633994924C10.889761548010028,27.063389129124516,10.17180504587759,27.75973368625546,10.17180504587759,27.75973368625546L10.60825193497385,29.383681270409397C12.207860084071399,28.168382814229517,13.977830309116143,28.710037765048583,14.95019719211541,28.552726882761817C17.055770143609152,28.228993027180877,18.060223647094062,26.43286111528778,19.879289130808502,26.982051198390764C17.174564387546525,28.35790077846741,17.6156913365503,29.39471121700626,16.401060728815537,30.180318637150776C15.57340121458492,30.690272543393803,14.352679667234952,30.97019196380643,13.402935042721765,31.072394184778204C12.453218710631518,31.174605299886025,11.007620191900969,31.92440346652984,11.007620191900969,31.92440346652984L11.862488256153483,33.44176202408926ZZ" style="fill: rgb(177, 247, 228);"/></g><g id="shape-b0db78f0-203d-11ec-9082-1597698bcafa"><path d="M26.367916328853426,21.541864617553983C25.752024052399975,21.08463850960743,24.874335589572183,21.20375348989637,24.407570785845564,21.80789217873189C23.940805982118945,22.41203086756559,24.061708216018815,23.272455921102846,24.677600492472266,23.7296820290494C25.293520690584955,24.186936521225107,26.17118070208835,24.06782099969587,26.63794550581588,23.46368231086126C27.104710839209474,22.859515779037793,26.983808605308695,21.999090725500537,26.367916328853426,21.541864617553983ZZ" style="stroke-width: 1; stroke: white;"/></g><g id="shape-b0db78f1-203d-11ec-9082-1597698bcafa"><path d="M22.290717374941778,16.541050556460505C23.171689612726368,15.546130552717841,25.542000733571513,14.462977670812506,27.935050108374526,16.237591904608962C30.328155856159356,18.01223506387396,29.602746949540233,20.315197630069633,28.945962813088045,21.476403721246243" style="stroke-width: 1; stroke: white;"/></g></g><g id="shape-b0dab5a0-203d-11ec-9082-1597698bcafa" width="101" height="114" fill="none"><g id="shape-b0dab5a1-203d-11ec-9082-1597698bcafa"><path d="M37.367513280913045,20.133292488741972C37.367513280913045,20.133292488741972,34.47324632323307,22.30653980059742,34.84863363869499,25.356438180496298C35.22402695335495,28.406336462321633,41.879424902602295,29.294056176220693,46.87282537913052,28.083008645277005C51.86624767451667,26.871984813337804,53.880832984758854,30.625859726819726,53.880832984758854,30.625859726819726L53.83623996239021,23.91664163630867C53.83623996239021,23.91664163630867,54.781440392099285,26.233739014499406,56.225371174116844,26.940924119365263C57.66919482759022,27.64811097553593,58.90788052119842,27.487660368157776,58.90788052119842,27.487660368157776L58.62188616189542,9.114836810032102C58.62188616189542,9.114836810032102,54.141558755091864,8.717594259259386,52.30578058531046,11.639492813587822C50.469980989820215,14.561391718178129,51.2541616945864,15.029686005809708,50.53323418202672,14.72450496800684C49.81228560116779,14.419346143432449,50.138951377463854,8.829826936177597,50.138951377463854,8.829826936177597C50.138951377463854,8.829826936177597,47.311935842496496,11.134827867714193,42.57887875848883,12.341595327473442C37.84582203188893,13.548384650201115,30.80430857443207,16.918822766548146,37.367513280913045,20.133292488741972ZZ" style="fill: rgb(250, 181, 245);"/></g><g id="shape-b0dab5a2-203d-11ec-9082-1597698bcafa"><path d="M43.78310756909741,21.898392426751343C43.71040464575253,21.169031458118297,43.07154757977423,20.637631775699447,42.35613013401144,20.711456496497703C41.64073411395748,20.785280867036818,41.11970689025202,21.43636973165303,41.19238874529674,22.165752913514552C41.265091668642526,22.895113882149417,41.9039494494391,23.426557290502387,42.61936689520098,23.35273256970413C43.33476255784535,23.278886336197502,43.85578942414304,22.627775608613774,43.78310756909741,21.898392426751343ZZ" style="stroke-width: 1; stroke: white;"/></g><g id="shape-b0dab5a3-203d-11ec-9082-1597698bcafa"><path d="M44.564542765303486,15.866730564347563C45.95380390293849,15.817601817573632,48.460070218461624,16.85388775037427,48.7739403824462,20.003267049277383C49.087810903840364,23.152668211146192,46.79729976525141,24.281104032503208,45.437423948055766,24.625374981523237" style="stroke-width: 1; stroke: white;"/></g></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/ph-file.svg b/frontend/resources/images/ph-file.svg new file mode 100644 index 000000000..6859f17f5 --- /dev/null +++ b/frontend/resources/images/ph-file.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="206.95680443620125" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-1.9573043775739962 -2.7988168413976156 206.95680443620125 158.15679986166066" height="158.15679986166066" style="-webkit-print-color-adjust: exact;"><g id="shape-575c9093-25c2-11ec-9877-05429cda5971"><g id="shape-575c9094-25c2-11ec-9877-05429cda5971"><defs><mask id="outer-stroke-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" x="-1.9573043775739962" y="-2.7988168413976156" width="206.95680443620125" height="158.15679986166066" maskUnits="userSpaceOnUse"><use xlink:href="#stroke-shape-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" style="fill: none; stroke: white; stroke-width: 4;"/><use xlink:href="#stroke-shape-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" style="fill: black;"/></mask></defs><g class="outer-stroke-shape"><defs><rect width="201" height="152" x="1" id="stroke-shape-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" transform="matrix(0.9999999978661108,-1.745329248269963e-8,1.7453292541199837e-7,1.0000000006480119,-0.000013047912574393195,0.0000017222602934907627)" ry="3" rx="3" y="0" data-style="fill:none;stroke-width:2;stroke:#E3E3E3;stroke-opacity:1;stroke-dasharray:"/></defs><use xlink:href="#stroke-shape-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" mask="url(#outer-stroke-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35)" style="stroke-width: 4; stroke: rgb(227, 227, 227); stroke-opacity: 1; fill: none;"/><use xlink:href="#stroke-shape-ddd0b3b0-3bbe-11ec-880d-a73a7db30a35" style="fill: none;"/></g></g><g id="shape-575c9095-25c2-11ec-9877-05429cda5971"><path d="M0,91 h202 a0,0 0 0 1 0,0 v59 a3,3 0 0 1 -3,3 h-196 a3,3 0 0 1 -3,-3 v-59 a0,0 0 0 1 0,0 z" x="0" y="91" transform="matrix(1.0000000024760176,-1.0471975568525078e-7,3.490658523073093e-8,1.0000000103484785,-0.000004508681172410434,0.000009314180957176177)" width="202" height="62" style="fill: none; stroke-width: 2; stroke: rgb(227, 227, 227); stroke-opacity: 1;"/></g><g id="shape-575c9096-25c2-11ec-9877-05429cda5971"><rect rx="3" ry="3" x="10" y="103" transform="matrix(0.9999999995870766,-4.810081334728633e-16,-4.810081268554315e-16,1.000000030940994,3.1795153176972235e-8,-0.0000034653912877047333)" width="134" height="18" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g><g id="shape-575c9097-25c2-11ec-9877-05429cda5971"><rect rx="3" ry="3" x="10" y="131" transform="matrix(1.0000000022017677,6.948317137937551e-23,6.617444834426139e-23,1.0000000667740845,-2.234794180822064e-7,-0.000009114662532283546)" width="183" height="11" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/ph-left.svg b/frontend/resources/images/ph-left.svg new file mode 100644 index 000000000..7853a1b55 --- /dev/null +++ b/frontend/resources/images/ph-left.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="81.5877720738863" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.43500565160593396 0.14985215967590193 81.5877720738863 128.8502825397186" height="128.8502825397186" style="-webkit-print-color-adjust: exact;"><g id="shape-305b39c3-25c6-11ec-9877-05429cda5971" width="90" height="140" fill="none"><g id="shape-305b39c4-25c6-11ec-9877-05429cda5971"><path d="M18.180074555850297,34.13571661820788C16.328443710368447,37.802933917027985,15.363633970810042,41.85364142381468,15.363541785663983,45.96187246179579C15.363449600517924,49.407753224720636,16.04230101673238,52.81989422415609,17.360917347203213,56.00341606115035C18.679625862816465,59.187030083290665,20.612379636906553,62.07970778419531,23.049017419827578,64.51634556711724C25.485655202748603,66.95298335003963,28.378332903652336,68.88573712412926,31.561946925794473,70.20444563974297C34.74556094793297,71.52306197021062,38.157794132515846,72.20182120128084,41.603674895442964,72.20182120128084C48.56282376312447,72.20172901613478,55.23693615892262,69.43709648322647,60.15777926017472,64.51616119682512C65.07871454658016,59.59522591042378,67.84316270919953,52.92102132947775,67.8430705240462,45.96187246179579C67.84316270919953,41.85373360896074,66.87872171022173,37.802933917027985,65.027275235032,34.13571661820788L18.180074555850297,34.13571661820788ZZ" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g><g id="shape-305b39c5-25c6-11ec-9877-05429cda5971"><path d="M40.358622311596264,76.81790020937888C29.539497004501754,76.81799239452494,19.163413509784732,80.76582127821257,11.513152601448382,87.79300278375013C3.862873256086459,94.81972336355693,-0.4349826275210944,104.35074562355658,-0.43500383010177757,114.28922622951995C-0.4349070356984157,119.34558149560962,0.6793145450319571,124.34939122839569,2.840650607504358,129.00013185144462L77.87714712656452,129.00013185144462C80.03842787794929,124.34939122839569,81.15276192455713,119.34558149560962,81.15276192455713,114.28922622951995C81.15276192455713,109.36838312826512,80.09761074177368,104.49547630300549,78.04750527664146,99.94890489510135C75.99739981150924,95.40325533865871,72.99244060259662,91.27234675472573,69.20436857717868,87.79281841345801C65.41629655176075,84.31329007218983,60.919228567337996,81.55317461144296,55.96990025592095,79.67010863112546C51.02047975936148,77.78704265080796,45.715777709559916,76.81790020937888,40.358622311596264,76.81790020937888ZZ" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g><g id="shape-305b39c6-25c6-11ec-9877-05429cda5971"><path d="M22.63741074228892,55.77654841648564C22.63741074228892,55.77654841648564,28.038261899520876,66.05546876716517,39.71092947477882,65.88123884095012C51.38359705003313,65.70700891473462,57.30695361562539,56.473468121347196,57.30695361562539,56.473468121347196" style="stroke-width: 2; stroke: white;"/></g><g id="shape-305b39c7-25c6-11ec-9877-05429cda5971"><path d="M33.141447219852125,53.60300704066367C36.667529059923254,53.60300704066367,39.52591388646397,50.77716357071449,39.52591388646397,47.29136663950885C39.52591388646397,43.805569708302755,36.667529059923254,40.97972623835358,33.141447219852125,40.97972623835358C29.615457564927056,40.97972623835358,26.75698055324392,43.805569708302755,26.75698055324392,47.29136663950885C26.75698055324392,50.77716357071449,29.615457564927056,53.60300704066367,33.141447219852125,53.60300704066367ZZ" style="fill: rgb(227, 227, 227); fill-opacity: 1; stroke-width: 2; stroke: white;"/></g><g id="shape-305b39c8-25c6-11ec-9877-05429cda5971"><path d="M52.22921139562823,5.9902455968044706C47.897615748537646,2.9522656685448965,42.82503589672706,1.0223696339853632,37.493231413951435,0.383799439228369C32.16142693117581,-0.2547744429339218,26.74730111289682,0.4191478335183092,21.76469396372704,2.341603604932061L64.9989743951628,32.66471824116161C65.42634473269572,27.547336408323645,64.47748302341643,22.41059569479512,62.24254634026147,17.742063338540447C60.007609657106514,13.073623167432288,56.56080704272608,9.028299273180437,52.22921139562823,5.9902455968044706ZZ" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/images/ph-right.svg b/frontend/resources/images/ph-right.svg new file mode 100644 index 000000000..6e2852f31 --- /dev/null +++ b/frontend/resources/images/ph-right.svg @@ -0,0 +1 @@ +<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="102.12322708597458" xmlns="http://www.w3.org/2000/svg" id="screenshot" version="1.1" viewBox="-0.2830548330293823 0.00013327541864782688 102.12322708597458 54.02153523420793" height="54.02153523420793" style="-webkit-print-color-adjust: exact;"><g id="shape-305b39ca-25c6-11ec-9877-05429cda5971" width="133" height="70" fill="none"><g id="shape-305b39cb-25c6-11ec-9877-05429cda5971"><path d="M92.06001883709541,52.780900512351764C92.66931429164106,49.56773051235177,86.02942338254798,47.840623239624165,83.94800520073477,47.06227823962445C89.18168701891409,45.781289603260575,101.50092247346038,49.98474823962442,101.82747470073446,46.54621233053331C102.1541065189158,43.10765323962414,96.1049138370945,39.43658051235161,92.18095065527814,37.78441233053354C88.2568870189134,36.13216687598788,82.26786429164349,34.273371421442334,83.30053701891302,33.860271421442576C84.33313247346086,33.4472486941695,93.48809610982426,36.99089869416957,94.37155520073429,34.2504214214423C95.25488292800583,31.510021421442616,89.671905200732,29.82478051235148,85.57220065527872,28.284194148715414C74.89666429164208,24.27258051235185,76.95853247345804,0.00013505780589184724,76.95853247345804,0.00013505780589184724L-0.28305389017623384,0.486489603260452C-0.28305389017623384,1.519162330533618,10.851173382550769,39.85655778507862,33.94041883709724,49.143364148715136C49.09020065527875,55.740090512351344,63.87718701891572,49.16363278507879,75.95035520072997,51.06297323962417C82.07081883709907,52.068059603260735,91.45064610982081,55.99406278507877,92.06001883709541,52.780900512351764ZZ" style="fill: rgb(227, 227, 227); fill-opacity: 1;"/></g><g id="shape-305b39cc-25c6-11ec-9877-05429cda5971"><path d="M39.658059746187064,27.905789603260928C42.341277928004274,26.911598694169697,45.32354156436486,28.28403960326068,46.31920065527811,30.971275966897338C47.314859746184084,33.658435057805946,45.946823382550065,36.64278505780612,43.263605200732854,37.6369759668969C40.580387018912006,38.631089603260534,37.59812338255142,37.258648694170006,36.60246429163817,34.57148960326049C35.6068052007322,31.884253239624286,36.974841564369854,28.89990323962411,39.658059746187064,27.905789603260928ZZ" style="stroke-width: 2; stroke: white;"/></g><g id="shape-305b39cd-25c6-11ec-9877-05429cda5971"><path d="M59.423496109822736,14.73643051235149C57.15994610982307,9.680012330533373,49.1343233825537,2.492953239624512,37.54835974618618,6.785685057806404C25.962164291642694,11.078494148715436,26.02405974618887,21.21628960326052,27.201927928006626,26.674912330533516" style="stroke-width: 2; stroke: white;"/></g></g></svg> \ No newline at end of file diff --git a/frontend/resources/styles/main/partials/dashboard-grid.scss b/frontend/resources/styles/main/partials/dashboard-grid.scss index 032bab70c..bbf80ec67 100644 --- a/frontend/resources/styles/main/partials/dashboard-grid.scss +++ b/frontend/resources/styles/main/partials/dashboard-grid.scss @@ -335,6 +335,20 @@ padding: 3rem; justify-content: center; + &.drafts { + background-image: url("/images/ph-left.svg"), url("/images/ph-right.svg"); + background-position: 15% bottom, 85% top; + background-repeat: no-repeat; + .text { + p { + max-width: 360px; + text-align: center; + font-size: $fs16; + } + } + } + + svg { width: 36px; height: 36px; @@ -346,5 +360,10 @@ color: $color-gray-30; font-size: $fs16; } + + img.ph-files { + height: 150px; + margin-right: calc(100% - 148px); + } } diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index 27da8245d..bff16696c 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -63,7 +63,7 @@ display: flex; flex-direction: column; width: 448px; - background-color: $color-dashboard; + background-color: $color-white; .modal-header { align-items: center; @@ -705,7 +705,7 @@ background-color: $color-white; box-shadow: 0 10px 10px rgba(0,0,0,.2); display: flex; - min-height: 370px; + min-height: 420px; flex-direction: row; font-family: "sourcesanspro", sans-serif; min-width: 620px; @@ -824,21 +824,93 @@ } &.final { + // TODO: Juan revisa TODA esta parte + padding: $size-5 0 0 0; + flex-direction: column; + + .modal-top { + padding-top: 40px; + color: $color-gray-60; + display: flex; + flex-direction: column; + align-items: center; + + h1 { + font-family: 'worksans', sans-serif; + font-weight: 700; + font-size: 27px; + margin-bottom: $size-3; + } + p { + font-family: 'worksans', sans-serif; + font-weight: 500; + font-size: $fs18; + } + + } + + .modal-columns { + display: flex; + margin: 17px; + + .modal-left { + background-image: url("/images/on-solo.svg"); + background-position: left top; + background-size: 11%; + } + + .modal-left:hover { + background-image: url("/images/on-solo-hover.svg"); + background-size: 15%; + } + + .modal-right { + background-image: url("/images/on-teamup.svg"); + background-position: right top; + background-size: 28%; + } + + .modal-right:hover { + background-image: url("/images/on-teamup-hover.svg"); + background-size: 32%; + } + + .modal-right, + .modal-left { + background-repeat: no-repeat; + border-radius: $br-medium; + transition: all ease .3s; + &:hover { + background-color: $color-primary; + } + } + } + + .modal-left { + margin-right: 35px; + } .modal-left, .modal-right { + justify-content: center; align-items: center; background-color: $color-white; color: $color-black; flex: 1; flex-direction: column; - overflow: visible; - padding: $size-6 40px; + // overflow: visible; + // padding: $size-6 40px; text-align: center; + border: 1px solid $color-gray-10; + border-radius: 2px; + min-height: 180px; + width: 233px; + cursor: pointer; + h2 { - font-weight: 900; + font-weight: 700; margin-bottom: $size-5; font-size: $fs24; } @@ -847,12 +919,6 @@ font-size: $fs14; } - .btn-primary { - margin-bottom: 0; - margin-top: auto; - width: 200px; - } - img { box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25); border-radius: $br-medium; @@ -861,26 +927,6 @@ width: 150px; } } - - .modal-left { - border-right: 1px solid $color-gray-10; - - form { - align-items: center; - display: flex; - flex-direction: column; - margin-top: auto; - - .custom-input { - margin-bottom: $size-4; - - input { - width: 200px; - } - } - } - } - } } @@ -899,3 +945,193 @@ .relnotes .onboarding { height: 420px; } + +.onboarding-templates { + position: fixed; + top: 0; + right: 0; + width: 348px; + height: 100vh; + + .modal-close-button { + width: 34px; + height: 34px; + margin-right: 13px; + margin-top: 13px; + svg { + width: 24px; + height: 24px; + } + } + + .modal-header { + height: unset; + border-radius: unset; + justify-content: flex-end; + } + + .modal-content { + border: 0px; + padding: 0px 25px; + background-color: $color-white; + flex-grow: 1; + + p, h3 { + color: $color-gray-60; + text-align: center; + } + + h3 { + font-size: $fs18; + font-weight: bold; + } + + p { + font-size: $fs16; + } + + + .templates { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 8%; + } + + .template-item { + width: 275px; + border: 1px solid $color-gray-10; + + display: flex; + flex-direction: column; + text-align: left; + border-radius: $br-small; + + &:not(:last-child) { + margin-bottom: 22px; + } + } + + .template-item-content { + // height: 144px; + flex-grow: 1; + + img { + border-radius: $br-small $br-small 0 0; + } + } + + .template-item-title { + padding: 6px 12px; + height: 64px; + border-top: 1px solid $color-gray-10; + + .label { + color: $color-black; + padding: 0px 4px; + font-size: $fs16; + display: flex; + } + + .action { + color: $color-primary-dark; + cursor: pointer; + font-size: $fs14; + font-weight: 600; + display: flex; + justify-content: flex-end; + margin-top: $size-2; + } + + } + } +} + + +.onboarding-team { + display: flex; + min-width: 620px; + min-height: 420px; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + + .title { + display: flex; + flex-direction: column; + align-items: center; + width: 408px; + + color: $color-gray-60; + h2 { + font-weight: 700; + padding-bottom: 10px; + } + + p { + text-align: center; + font-size: $fs18; + } + } + + form { + display: flex; + flex-direction: column; + margin-top: $size-6; + + .buttons { + margin-top: 30px; + display: flex; + justify-content: flex-end; + + > *:not(:last-child) { + margin-right: 13px; + } + + input { margin-bottom: unset; } + input[type=submit] { + } + + .btn-primary { + width: 117px; + } + } + + .team-row { + .custom-input { + width: 459px; + } + } + + .invite-row { + display: flex; + justify-content: space-between; + + > *:not(:last-child) { + margin-right: 13px; + } + + .custom-input { + width: 321px; + } + + .custom-select { + width: 118px; + } + } + + .skip-action { + display: flex; + justify-content: flex-end; + margin-top: 15px; + .action { + color: $color-primary-dark; + font-weight: 500; + font-size: $fs16; + cursor: pointer; + } + } + + } +} diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 7025dc5ea..e15b8d7fb 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -300,6 +300,28 @@ (rx/map team-created) (rx/catch on-error)))))) +;; --- EVENT: create-team-with-invitations + +;; NOTE: right now, it only handles a single email, in a near future +;; this will be changed to the ability to specify multiple emails. + +(defn create-team-with-invitations + [{:keys [name email role] :as params}] + (us/assert string? name) + (ptk/reify ::create-team-with-invitations + ptk/WatchEvent + (watch [_ _ _] + (let [{:keys [on-success on-error] + :or {on-success identity + on-error rx/throw}} (meta params) + params {:name name + :emails #{email} + :role role}] + (->> (rp/mutation! :create-team-and-invite-members params) + (rx/tap on-success) + (rx/map team-created) + (rx/catch on-error)))))) + ;; --- EVENT: update-team (defn update-team diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 3612d8490..adc3cfaf6 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -61,12 +61,6 @@ (def dashboard-search-result (l/derived :dashboard-search-result st/state)) -(def dashboard-team - (l/derived (fn [state] - (let [team-id (:current-team-id state)] - (get-in state [:teams team-id]))) - st/state)) - (def dashboard-team-stats (l/derived :dashboard-team-stats st/state)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index b005dc11c..545471615 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -72,7 +72,10 @@ :dashboard-team-settings) [:* #_[:div.modal-wrapper - [:& app.main.ui.onboarding/release-notes-modal {:version "1.8"}]] + #_[:& app.main.ui.onboarding/onboarding-templates-modal] + #_[:& app.main.ui.onboarding/onboarding-modal] + #_[:& app.main.ui.onboarding/onboarding-team-modal] + ] [:& dashboard {:route route}]] :viewer diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 9d2f00178..1292c6eaf 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -104,11 +104,11 @@ (when (and (:onboarding-viewed props) (not= version (:main @cf/version)) (not= "0.0" (:main @cf/version))) - (tm/schedule 1000 #(st/emit! (modal/show {:type :release-notes :version (:main @cf/version)}))))))) + (tm/schedule 1000 #(st/emit! (modal/show {:type :release-notes + :version (:main @cf/version)}))))))) [:& (mf/provider ctx/current-team-id) {:value team-id} [:& (mf/provider ctx/current-project-id) {:value project-id} - ;; NOTE: dashboard events and other related functions assumes ;; that the team is a implicit context variable that is ;; available using react context or accessing diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 4ce733033..ef413c78c 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -120,6 +120,6 @@ [:* [:& header {:team team :project project}] [:section.dashboard-container - [:& grid {:project-id (:id project) + [:& grid {:project project :files files}]]])) diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index 3bd48a1ce..f91e1de5c 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -15,6 +15,7 @@ [app.main.ui.dashboard.file-menu :refer [file-menu]] [app.main.ui.dashboard.import :refer [use-import-file]] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] + [app.main.ui.dashboard.placeholder :refer [empty-placeholder loading-placeholder]] [app.main.ui.icons :as i] [app.main.worker :as wrk] [app.util.dom :as dom] @@ -195,24 +196,10 @@ :on-edit on-edit :on-menu-close on-menu-close}])]]])) -(mf/defc empty-placeholder - [{:keys [dragging?] :as props}] - (if-not dragging? - [:div.grid-empty-placeholder - [:div.icon i/file-html] - [:div.text (tr "dashboard.empty-files")]] - [:div.grid-row.no-wrap - [:div.grid-item]])) - -(mf/defc loading-placeholder - [] - [:div.grid-empty-placeholder - [:div.icon i/loader] - [:div.text (tr "dashboard.loading-files")]]) - (mf/defc grid - [{:keys [files project-id] :as props}] - (let [dragging? (mf/use-state false) + [{:keys [files project] :as props}] + (let [dragging? (mf/use-state false) + project-id (:id project) on-finish-import (mf/use-callback @@ -272,7 +259,7 @@ :navigate? true}])] :else - [:& empty-placeholder])])) + [:& empty-placeholder {:default? (:is-default project)}])])) (mf/defc line-grid-row [{:keys [files selected-files on-load-more dragging?] :as props}] @@ -330,8 +317,11 @@ (tr "dashboard.show-all-files")]])])) (mf/defc line-grid - [{:keys [project-id team-id files on-load-more] :as props}] + [{:keys [project team files on-load-more] :as props}] (let [dragging? (mf/use-state false) + project-id (:id project) + team-id (:id team) + selected-files (mf/deref refs/dashboard-selected-files) selected-project (mf/deref refs/dashboard-selected-project) @@ -413,5 +403,6 @@ :dragging? @dragging?}] :else - [:& empty-placeholder {:dragging? @dragging?}])])) + [:& empty-placeholder {:dragging? @dragging? + :default? (:is-default project)}])])) diff --git a/frontend/src/app/main/ui/dashboard/placeholder.cljs b/frontend/src/app/main/ui/dashboard/placeholder.cljs new file mode 100644 index 000000000..1df3839bd --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/placeholder.cljs @@ -0,0 +1,34 @@ +;; 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/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.ui.dashboard.placeholder + (:require + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [tr]] + [rumext.alpha :as mf])) + +(mf/defc empty-placeholder + [{:keys [dragging? default?] :as props}] + (cond + (true? dragging?) + [:div.grid-row.no-wrap + [:div.grid-item]] + + (true? default?) + [:div.grid-empty-placeholder.drafts + [:div.text + [:& i18n/tr-html {:label "dashboard.empty-placeholder-drafts"}]]] + + :else + [:div.grid-empty-placeholder + [:img.ph-files {:src "images/ph-file.svg"}]])) + +(mf/defc loading-placeholder + [] + [:div.grid-empty-placeholder + [:div.icon i/loader] + [:div.text (tr "dashboard.loading-files")]]) + diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index b3654ff59..e1820cc1a 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -73,7 +73,6 @@ :accept-label (tr "modals.delete-project-confirm.accept") :on-accept delete-fn})) - file-input (mf/use-ref nil) on-import-files diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 7089ba721..c171aab38 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -33,10 +33,8 @@ (tr "dashboard.new-project")]])) (mf/defc project-item - [{:keys [project first? files] :as props}] + [{:keys [project first? team files] :as props}] (let [locale (mf/deref i18n/locale) - - team-id (:team-id project) file-count (or (:count project) 0) dstate (mf/deref refs/dashboard-local) @@ -145,9 +143,8 @@ i/actions]] [:& line-grid - {:project-id (:id project) - :project project - :team-id team-id + {:project project + :team team :on-load-more on-nav :files files}]])) @@ -186,7 +183,8 @@ (filterv #(= id (:project-id %))) (sort-by :modified-at #(compare %2 %1))))] [:& project-item {:project project - :files files + :team team + :files files :first? (= project (first projects)) :key (:id project)}]))]]))) diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index b07e3dd57..e4ae3e4bc 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -72,8 +72,9 @@ #(doseq [key keys] (events/unlistenByKey key))))) - [:div.modal-wrapper {:ref wrapper-ref} - (mf/element (get components (:type data)) (:props data))])) + (when-let [component (get components (:type data))] + [:div.modal-wrapper {:ref wrapper-ref} + (mf/element component (:props data))]))) (def modal-ref diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index c980f45c0..e0f6e1066 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -12,8 +12,10 @@ [app.main.data.messages :as dm] [app.main.data.modal :as modal] [app.main.data.users :as du] + [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.icons :as i] [app.main.ui.releases.common :as rc] [app.main.ui.releases.v1-4] [app.main.ui.releases.v1-5] @@ -21,10 +23,13 @@ [app.main.ui.releases.v1-7] [app.main.ui.releases.v1-8] [app.main.ui.releases.v1-9] + [app.util.dom :as dom] + [app.util.http :as http] [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] [app.util.router :as rt] [app.util.timers :as tm] + [beicon.core :as rx] [cljs.spec.alpha :as s] [rumext.alpha :as mf])) @@ -159,7 +164,7 @@ skip (mf/use-callback (st/emitf (modal/hide) - (modal/show {:type :onboarding-team}) + (modal/show {:type :onboarding-choice}) (du/mark-onboarding-as-viewed)))] (mf/use-layout-effect @@ -187,57 +192,232 @@ (s/def ::team-form (s/keys :req-un [::name])) +(mf/defc onboarding-choice-modal + {::mf/register modal/components + ::mf/register-as :onboarding-choice} + [] + (let [;; When user choices the option of `fly solo`, we proceed to show + ;; the onboarding templates modal. + on-fly-solo + (fn [] + (tm/schedule 400 #(st/emit! (modal/show {:type :onboarding-templates})))) + + ;; When user choices the option of `team up`, we proceed to show + ;; the team creation modal. + on-team-up + (fn [] + (st/emit! (modal/show {:type :onboarding-team}))) + ] + + [:div.modal-overlay + [:div.modal-container.onboarding.final.animated.fadeInUp + [:div.modal-top + [:h1 (tr "onboarding.welcome.title")] + [:p (tr "onboarding.welcome.desc3")]] + [:div.modal-columns + [:div.modal-left + [:div.content-button {:on-click on-fly-solo} + [:h2 (tr "onboarding.choice.fly-solo")] + [:p (tr "onboarding.choice.fly-solo-desc")]]] + [:div.modal-right + [:div.content-button {:on-click on-team-up} + [:h2 (tr "onboarding.choice.team-up")] + [:p (tr "onboarding.choice.team-up-desc")]]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]])) + (mf/defc onboarding-team-modal {::mf/register modal/components ::mf/register-as :onboarding-team} [] - (let [close (mf/use-fn (st/emitf (modal/hide))) - form (fm/use-form :spec ::team-form + (let [form (fm/use-form :spec ::team-form :initial {}) + on-submit + (mf/use-callback + (fn [form _] + (let [tname (get-in @form [:clean-data :name])] + (st/emit! (modal/show {:type :onboarding-team-invitations :name tname})))))] + + [:div.modal-overlay + [:div.modal-container.onboarding-team + [:div.title + [:h2 (tr "onboarding.choice.team-up")] + [:p (tr "onboarding.choice.team-up-desc")]] + + [:& fm/form {:form form + :on-submit on-submit} + + [:div.team-row + [:& fm/input {:type "text" + :name :name + :label (tr "onboarding.team-input-placeholder")}]] + + [:div.buttons + [:button.btn-secondary.btn-large + {:on-click #(st/emit! (modal/show {:type :onboarding-choice}))} + (tr "labels.cancel")] + [:& fm/submit-button + {:label (tr "labels.next")}]]] + + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]])) + +(defn get-available-roles + [] + [{:value "editor" :label (tr "labels.editor")} + {:value "admin" :label (tr "labels.admin")}]) + +(s/def ::email ::us/email) +(s/def ::role ::us/keyword) +(s/def ::invite-form + (s/keys :req-un [::role ::email])) + +;; This is the final step of team creation, consists in provide a +;; shortcut for invite users. + +(mf/defc onboarding-team-invitations-modal + {::mf/register modal/components + ::mf/register-as :onboarding-team-invitations} + [{:keys [name] :as props}] + (let [initial (mf/use-memo (constantly + {:role "editor" + :name name})) + form (fm/use-form :spec ::invite-form + :initial initial) + + roles (mf/use-memo #(get-available-roles)) + on-success (mf/use-callback (fn [_form response] - (st/emit! (modal/hide) - (rt/nav :dashboard-projects {:team-id (:id response)})))) + (let [project-id (:default-project-id response) + team-id (:id response)] + (st/emit! + (modal/hide) + (rt/nav :dashboard-projects {:team-id team-id})) + (tm/schedule 400 #(st/emit! + (modal/show {:type :onboarding-templates + :project-id project-id})))))) on-error (mf/use-callback (fn [_form _response] (st/emit! (dm/error "Error on creating team.")))) - on-submit + ;; The SKIP branch only creates the team, without invitations + on-skip (mf/use-callback - (fn [form _event] + (fn [_] (let [mdata {:on-success (partial on-success form) :on-error (partial on-error form)} - params {:name (get-in @form [:clean-data :name])}] - (st/emit! (dd/create-team (with-meta params mdata))))))] + params {:name name}] + (st/emit! (dd/create-team (with-meta params mdata)))))) + + ;; The SUBMIT branch creates the team with the invitations + on-submit + (mf/use-callback + (fn [form _] + (let [mdata {:on-success (partial on-success form) + :on-error (partial on-error form)} + params (:clean-data @form)] + (st/emit! (dd/create-team-with-invitations (with-meta params mdata))))))] [:div.modal-overlay - [:div.modal-container.onboarding.final.animated.fadeInUp - [:div.modal-left - [:img {:src "images/onboarding-team.jpg" :border "0" :alt (tr "onboarding.team.create.title")}] - [:h2 (tr "onboarding.team.create.title")] - [:p (tr "onboarding.team.create.desc1")] + [:div.modal-container.onboarding-team + [:div.title + [:h2 (tr "onboarding.choice.team-up")] + [:p (tr "onboarding.choice.team-up-desc")]] - [:& fm/form {:form form - :on-submit on-submit} - [:& fm/input {:type "text" - :name :name - :label (tr "onboarding.team.create.input-placeholder")}] + [:& fm/form {:form form + :on-submit on-submit} + + [:div.invite-row + [:& fm/input {:name :email + :label (tr "labels.email")}] + [:& fm/select {:name :role + :options roles}]] + + [:div.buttons + [:button.btn-secondary.btn-large + {:on-click #(st/emit! (modal/show {:type :onboarding-choice}))} + (tr "labels.cancel")] [:& fm/submit-button - {:label (tr "onboarding.team.create.button")}]]] - - [:div.modal-right - [:img {:src "images/onboarding-start.jpg" :border "0" :alt (tr "onboarding.team.start.title")}] - [:h2 (tr "onboarding.team.start.title")] - [:p (tr "onboarding.team.start.desc1")] - [:button.btn-primary.btn-large {:on-click close} (tr "onboarding.team.start.button")]] - - + {:label (tr "labels.create")}]] + [:div.skip-action + {:on-click on-skip} + [:div.action "Skip and invite later"]]] [:img.deco {:src "images/deco-left.png" :border "0"}] [:img.deco.right {:src "images/deco-right.png" :border "0"}]]])) +(mf/defc template-item + [{:keys [name path image project-id]}] + (let [downloading? (mf/use-state false) + link (str (assoc cf/public-uri :path path)) + + on-finish-import + (fn [] + (st/emit! (dd/fetch-recent-files))) + + open-import-modal + (fn [file] + (st/emit! (modal/show + {:type :import + :project-id project-id + :files [file] + :on-finish-import on-finish-import}))) + on-click + (fn [] + (reset! downloading? true) + (->> (http/send! {:method :get :uri link :response-type :blob :mode :no-cors}) + (rx/subs (fn [{:keys [body] :as response}] + (open-import-modal {:name name :uri (dom/create-uri body)})) + (fn [error] + (js/console.log "error" error)) + (fn [] + (reset! downloading? false))))) + ] + + [:div.template-item + [:div.template-item-content + [:img {:src image}]] + [:div.template-item-title + [:div.label name] + (if @downloading? + [:div.action "Fetching..."] + [:div.action {:on-click on-click} "+ Add to drafts"])]])) + +(mf/defc onboarding-templates-modal + {::mf/wrap-props false + ::mf/register modal/components + ::mf/register-as :onboarding-templates} + ;; NOTE: the project usually comes empty, it only comes fullfilled + ;; when a user creates a new team just after signup. + [{:keys [project-id] :as props}] + (let [close-fn (mf/use-callback #(st/emit! (modal/hide))) + profile (mf/deref refs/profile) + project-id (or project-id (:default-project-id profile))] + [:div.modal-overlay + [:div.modal-container.onboarding-templates + [:div.modal-header + [:div.modal-close-button + {:on-click close-fn} i/close]] + + [:div.modal-content + [:h3 (tr "onboarding.templates.title")] + [:p (tr "onboarding.templates.subtitle")] + + [:div.templates + [:& template-item + {:path "/github/penpot-files/Penpot-Design-system.penpot" + :image "https://penpot.app/images/libraries/cover-ds-penpot.jpg" + :name "Penpot Design System" + :project-id project-id}] + [:& template-item + {:path "/github/penpot-files/Material-Design-Kit.penpot" + :image "https://penpot.app/images/libraries/cover-material.jpg" + :name "Material Design Kit" + :project-id project-id}]]]]])) + ;;; --- RELEASE NOTES MODAL diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index d36ec13a6..04c9521f3 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -169,7 +169,7 @@ (rx/tap #(do (swap! current inc) (progress! context :upload-data @current total)))))) - + (rx/map first) (rx/tap #(reset! revn (:revn %))) (rx/ignore)) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 691cc14e6..7297aa88b 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -250,6 +250,12 @@ msgstr "Duplicate %s files" msgid "dashboard.empty-files" msgstr "You still have no files here" + +#: src/app/main/ui/dashboard/grid.cljs +#, markdown +msgid "dashboard.empty-placeholder-drafts" +msgstr "Oh no! You have no files jet!</br> If you want to try with some templates go to [templates.penpot.app](https://penpot.app/libraries-templates.html)" + msgid "dashboard.export-frames" msgstr "Export artboards to PDF..." @@ -1533,6 +1539,13 @@ msgstr "Profile saved successfully!" msgid "notifications.validation-email-sent" msgstr "Verification email sent to %s. Check your email!" + +msgid "onboarding.templates.title" +msgstr "Start designing" + +msgid "onboarding.templates.subtitle" +msgid "Here are some templates." + msgid "onboarding.contrib.alt" msgstr "Open Source" @@ -1606,31 +1619,27 @@ msgstr "" msgid "onboarding.slide.3.title" msgstr "One shared source of truth" -msgid "onboarding.team.create.button" -msgstr "Create a team" +msgid "onboarding.choice.fly-solo" +msgstr "Fly solo" -msgid "onboarding.team.create.desc1" -msgstr "" -"Are you working with someone? Create a team to work together on projects " -"and share design assets." +msgid "onboarding.choice.fly-solo-desc" +msgstr "Jump away into Penpot and start designing by your own." -msgid "onboarding.team.create.input-placeholder" +msgid "onboarding.choice.team-up" +msgstr "Team up" + +msgid "onboarding.team.skip-and-invite-later" +msgstr "Skip and invite later" + +msgid "onboarding.choice.team-up-desc" +msgstr "Are you working with someone? Create a team and invite people to work together on projects and share design assets." + +msgid "labels.next" +msgstr "Next" + +msgid "onboarding.team-input-placeholder" msgstr "Enter new team name" -msgid "onboarding.team.create.title" -msgstr "Create team" - -msgid "onboarding.team.start.button" -msgstr "Start right away" - -msgid "onboarding.team.start.desc1" -msgstr "" -"Jump right away into Penpot and start designing by your own. You will still " -"have the chance to create teams later." - -msgid "onboarding.team.start.title" -msgstr "Start designing" - msgid "onboarding.welcome.alt" msgstr "Penpot" @@ -1642,6 +1651,9 @@ msgstr "" "Penpot is still at development stage and there will be constant updates. We " "hope you enjoy the first stable version." +msgid "onboarding.welcome.desc3" +msgstr "How do you want to start?" + msgid "onboarding.welcome.title" msgstr "Welcome to Penpot!" @@ -3187,4 +3199,5 @@ msgid "workspace.updates.update" msgstr "Update" msgid "workspace.viewport.click-to-close-path" -msgstr "Click to close the path" \ No newline at end of file +msgstr "Click to close the path" +