mirror of
https://github.com/penpot/penpot.git
synced 2025-03-17 10:11:22 -05:00
♻️ Refactor error handling.
This commit is contained in:
parent
b4ba9d4375
commit
bea093e8da
17 changed files with 578 additions and 334 deletions
|
@ -10,20 +10,25 @@
|
|||
(ns app.http.errors
|
||||
"A errors handling for the http server."
|
||||
(:require
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[app.common.exceptions :as ex]
|
||||
[clojure.tools.logging :as log]
|
||||
[cuerdas.core :as str]
|
||||
[expound.alpha :as expound]))
|
||||
|
||||
|
||||
(defn get-context-string
|
||||
[request edata]
|
||||
(str "=| uri: " (pr-str (:uri request)) "\n"
|
||||
"=| method: " (pr-str (:request-method request)) "\n"
|
||||
"=| params: " (pr-str (:params request)) "\n"
|
||||
"=| params: \n"
|
||||
(with-out-str
|
||||
(pprint (:params request)))
|
||||
"\n"
|
||||
|
||||
(when (map? edata)
|
||||
(str "=| ex-data: " (pr-str edata) "\n"))
|
||||
(str "=| ex-data: \n"
|
||||
(with-out-str
|
||||
(pprint edata))))
|
||||
|
||||
"\n"))
|
||||
|
||||
|
@ -33,69 +38,60 @@
|
|||
(or (:type edata)
|
||||
(class err)))))
|
||||
|
||||
(defmethod handle-exception :authorization
|
||||
[err _]
|
||||
{:status 403
|
||||
:body (ex-data err)})
|
||||
|
||||
(defmethod handle-exception :authentication
|
||||
[err _]
|
||||
{:status 401
|
||||
:body (ex-data err)})
|
||||
{:status 401 :body (ex-data err)})
|
||||
|
||||
(defn- explain-error
|
||||
[error]
|
||||
(with-out-str
|
||||
(expound/printer (:data error))))
|
||||
|
||||
(defmethod handle-exception :validation
|
||||
[err req]
|
||||
(let [header (get-in req [:headers "accept"])
|
||||
edata (ex-data err)]
|
||||
(cond
|
||||
(= :spec-validation (:code edata))
|
||||
(if (str/starts-with? header "text/html")
|
||||
{:status 400
|
||||
:headers {"content-type" "text/html"}
|
||||
:body (str "<pre style='font-size:16px'>"
|
||||
(with-out-str
|
||||
(expound/printer (:data edata)))
|
||||
"</pre>\n")}
|
||||
{:status 400
|
||||
:body (assoc edata :explain (with-out-str (expound/printer (:data edata))))})
|
||||
|
||||
:else
|
||||
(if (and (= :spec-validation (:code edata))
|
||||
(str/starts-with? header "text/html"))
|
||||
{:status 400
|
||||
:body edata})))
|
||||
:headers {"content-type" "text/html"}
|
||||
:body (str "<pre style='font-size:16px'>"
|
||||
(explain-error edata)
|
||||
"</pre>\n")}
|
||||
{:status 400
|
||||
:body (cond-> edata
|
||||
(map? (:data edata))
|
||||
(-> (assoc :explain (explain-error edata))
|
||||
(dissoc :data)))})))
|
||||
|
||||
(defmethod handle-exception :assertion
|
||||
[error request]
|
||||
(let [edata (ex-data error)]
|
||||
(log/error error
|
||||
(str "Assertion error\n"
|
||||
(get-context-string request edata)
|
||||
(with-out-str (expound/printer (:data edata)))))
|
||||
(str "Internal error: assertion\n"
|
||||
(get-context-string request edata)
|
||||
(explain-error edata)))
|
||||
{:status 500
|
||||
:body (assoc edata :explain (with-out-str (expound/printer (:data edata))))}))
|
||||
:body {:type :server-error
|
||||
:data (-> edata
|
||||
(assoc :explain (explain-error edata))
|
||||
(dissoc :data))}}))
|
||||
|
||||
(defmethod handle-exception :not-found
|
||||
[err _]
|
||||
(let [response (ex-data err)]
|
||||
{:status 404
|
||||
:body response}))
|
||||
|
||||
(defmethod handle-exception :service-error
|
||||
[err req]
|
||||
(handle-exception (.getCause ^Throwable err) req))
|
||||
|
||||
{:status 404 :body (ex-data err)})
|
||||
|
||||
(defmethod handle-exception :default
|
||||
[error request]
|
||||
(let [edata (ex-data error)]
|
||||
(log/error error
|
||||
(str "Internal Error\n"
|
||||
(get-context-string request edata)))
|
||||
(if (nil? edata)
|
||||
{:status 500
|
||||
:body {:type :server-error
|
||||
:hint (ex-message error)}}
|
||||
{:status 500
|
||||
:body (dissoc edata :data)})))
|
||||
(str "Internal Error: "
|
||||
(ex-message error)
|
||||
(get-context-string request edata)))
|
||||
{:status 500
|
||||
:body {:type :server-error
|
||||
:hint (ex-message error)
|
||||
:data edata}}))
|
||||
|
||||
(defn handle
|
||||
[error req]
|
||||
|
|
|
@ -108,7 +108,8 @@
|
|||
(proj/check-edition-permissions! conn profile-id id)
|
||||
(db/update! conn :project
|
||||
{:name name}
|
||||
{:id id})))
|
||||
{:id id})
|
||||
nil))
|
||||
|
||||
;; --- Mutation: Delete Project
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
[conn file-id page-id token]
|
||||
(let [sql "select exists(select 1 from file_share_token where file_id=? and page_id=? and token=?) as exists"]
|
||||
(when-not (:exists (db/exec-one! conn [sql file-id page-id token]))
|
||||
(ex/raise :type :authorization
|
||||
:code :unauthorized-token))))
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found))))
|
||||
|
||||
(defn retrieve-shared-token
|
||||
[conn file-id page-id]
|
||||
|
|
|
@ -140,13 +140,14 @@
|
|||
[spec x message context]
|
||||
(if (s/valid? spec x)
|
||||
x
|
||||
(let [data (s/explain-data spec x)
|
||||
hint (with-out-str (s/explain-out data))]
|
||||
(let [data (s/explain-data spec x)
|
||||
explain (with-out-str (s/explain-out data))]
|
||||
(ex/raise :type :assertion
|
||||
:code :spec-validation
|
||||
:hint message
|
||||
:data data
|
||||
:hint hint
|
||||
:explain explain
|
||||
:context context
|
||||
:message message
|
||||
#?@(:cljs [:stack (.-stack (ex-info message {}))])))))
|
||||
|
||||
|
||||
|
@ -181,13 +182,13 @@
|
|||
[spec data]
|
||||
(let [result (s/conform spec data)]
|
||||
(when (= result ::s/invalid)
|
||||
(let [data (s/explain-data spec data)
|
||||
hint (with-out-str
|
||||
(s/explain-out data))]
|
||||
(let [data (s/explain-data spec data)
|
||||
explain (with-out-str
|
||||
(s/explain-out data))]
|
||||
(throw (ex/error :type :validation
|
||||
:code :spec-validation
|
||||
:data data
|
||||
:hint hint))))
|
||||
:explain explain
|
||||
:data data))))
|
||||
result))
|
||||
|
||||
(defmacro instrument!
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
}
|
||||
},
|
||||
"auth.create-demo-account" : {
|
||||
"used-in" : [ "src/app/main/ui/auth/login.cljs:160", "src/app/main/ui/auth/register.cljs:136" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:136", "src/app/main/ui/auth/login.cljs:160" ],
|
||||
"translations" : {
|
||||
"en" : "Create demo account",
|
||||
"fr" : "Vous voulez juste essayer?",
|
||||
|
@ -27,7 +27,7 @@
|
|||
}
|
||||
},
|
||||
"auth.create-demo-profile" : {
|
||||
"used-in" : [ "src/app/main/ui/auth/login.cljs:157", "src/app/main/ui/auth/register.cljs:133" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:133", "src/app/main/ui/auth/login.cljs:157" ],
|
||||
"translations" : {
|
||||
"en" : "Just wanna try it?",
|
||||
"fr" : "Vous voulez juste essayer?",
|
||||
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
},
|
||||
"auth.email" : {
|
||||
"used-in" : [ "src/app/main/ui/auth/login.cljs:99", "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47", "src/app/main/ui/auth/login.cljs:99" ],
|
||||
"translations" : {
|
||||
"en" : "Email",
|
||||
"fr" : "Adresse email",
|
||||
|
@ -196,7 +196,7 @@
|
|||
}
|
||||
},
|
||||
"auth.password" : {
|
||||
"used-in" : [ "src/app/main/ui/auth/login.cljs:106", "src/app/main/ui/auth/register.cljs:106" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:106", "src/app/main/ui/auth/login.cljs:106" ],
|
||||
"translations" : {
|
||||
"en" : "Password",
|
||||
"fr" : "Mot de passe",
|
||||
|
@ -259,7 +259,7 @@
|
|||
}
|
||||
},
|
||||
"auth.register-submit" : {
|
||||
"used-in" : [ "src/app/main/ui/auth/login.cljs:134", "src/app/main/ui/auth/register.cljs:110" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:110", "src/app/main/ui/auth/login.cljs:134" ],
|
||||
"translations" : {
|
||||
"en" : "Create an account",
|
||||
"fr" : "Créer un compte",
|
||||
|
@ -295,7 +295,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.add-shared" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:225", "src/app/main/ui/dashboard/grid.cljs:180" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:225", "src/app/main/ui/dashboard/grid.cljs:182" ],
|
||||
"translations" : {
|
||||
"en" : "Add as Shared Library",
|
||||
"fr" : "",
|
||||
|
@ -343,7 +343,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.empty-files" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:187" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:189" ],
|
||||
"translations" : {
|
||||
"en" : "You still have no files here",
|
||||
"fr" : "Vous n'avez encore aucun fichier ici",
|
||||
|
@ -517,7 +517,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.num-of-members" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:299" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:301" ],
|
||||
"translations" : {
|
||||
"en" : "%s members",
|
||||
"es" : "%s integrantes"
|
||||
|
@ -542,7 +542,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.promote-to-owner" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:196" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:198" ],
|
||||
"translations" : {
|
||||
"en" : "Promote to owner",
|
||||
"es" : "Promover a dueño"
|
||||
|
@ -558,7 +558,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.remove-shared" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:223", "src/app/main/ui/dashboard/grid.cljs:179" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/header.cljs:223", "src/app/main/ui/dashboard/grid.cljs:181" ],
|
||||
"translations" : {
|
||||
"en" : "Remove as Shared Library",
|
||||
"fr" : "",
|
||||
|
@ -603,7 +603,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.show-all-files" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:250" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:252" ],
|
||||
"translations" : {
|
||||
"en" : "Show all files",
|
||||
"es" : "Ver todos los ficheros"
|
||||
|
@ -626,21 +626,21 @@
|
|||
}
|
||||
},
|
||||
"dashboard.team-info" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:282" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:284" ],
|
||||
"translations" : {
|
||||
"en" : "Team info",
|
||||
"es" : "Información del equipo"
|
||||
}
|
||||
},
|
||||
"dashboard.team-members" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:293" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:295" ],
|
||||
"translations" : {
|
||||
"en" : "Team members",
|
||||
"es" : "Integrantes del equipo"
|
||||
}
|
||||
},
|
||||
"dashboard.team-projects" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:302" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:304" ],
|
||||
"translations" : {
|
||||
"en" : "Team projects",
|
||||
"es" : "Proyectos del equipo"
|
||||
|
@ -674,7 +674,7 @@
|
|||
}
|
||||
},
|
||||
"dashboard.update-settings" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/options.cljs:72", "src/app/main/ui/settings/profile.cljs:82", "src/app/main/ui/settings/password.cljs:96" ],
|
||||
"used-in" : [ "src/app/main/ui/settings/profile.cljs:82", "src/app/main/ui/settings/password.cljs:96", "src/app/main/ui/settings/options.cljs:72" ],
|
||||
"translations" : {
|
||||
"en" : "Update settings",
|
||||
"fr" : "Mettre à jour les paramètres",
|
||||
|
@ -796,7 +796,7 @@
|
|||
}
|
||||
},
|
||||
"errors.clipboard-not-implemented" : {
|
||||
"used-in" : [ "src/app/main/data/workspace.cljs:1251" ],
|
||||
"used-in" : [ "src/app/main/data/workspace.cljs:1375" ],
|
||||
"translations" : {
|
||||
"en" : "Your browser cannot do this operation, please use Ctrl-V",
|
||||
"fr" : "",
|
||||
|
@ -805,7 +805,7 @@
|
|||
}
|
||||
},
|
||||
"errors.email-already-exists" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/change_email.cljs:47", "src/app/main/ui/auth/verify_token.cljs:80" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/verify_token.cljs:80", "src/app/main/ui/settings/change_email.cljs:47" ],
|
||||
"translations" : {
|
||||
"en" : "Email already used",
|
||||
"fr" : "Adresse e-mail déjà utilisée",
|
||||
|
@ -832,7 +832,7 @@
|
|||
}
|
||||
},
|
||||
"errors.generic" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/options.cljs:32", "src/app/main/ui/settings/profile.cljs:42", "src/app/main/ui/auth/verify_token.cljs:89" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/verify_token.cljs:89", "src/app/main/ui/settings/profile.cljs:42", "src/app/main/ui/settings/options.cljs:32" ],
|
||||
"translations" : {
|
||||
"en" : "Something wrong has happened.",
|
||||
"fr" : "Quelque chose c'est mal passé.",
|
||||
|
@ -859,7 +859,7 @@
|
|||
}
|
||||
},
|
||||
"errors.media-type-mismatch" : {
|
||||
"used-in" : [ "src/app/main/data/media.cljs:78", "src/app/main/data/workspace/persistence.cljs:394" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:381", "src/app/main/data/media.cljs:78" ],
|
||||
"translations" : {
|
||||
"en" : "Seems that the contents of the image does not match the file extension.",
|
||||
"fr" : "",
|
||||
|
@ -868,7 +868,7 @@
|
|||
}
|
||||
},
|
||||
"errors.media-type-not-allowed" : {
|
||||
"used-in" : [ "src/app/main/data/media.cljs:75", "src/app/main/data/workspace/persistence.cljs:391" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:378", "src/app/main/data/media.cljs:75" ],
|
||||
"translations" : {
|
||||
"en" : "Seems that this is not a valid image.",
|
||||
"fr" : "",
|
||||
|
@ -913,7 +913,7 @@
|
|||
}
|
||||
},
|
||||
"errors.unexpected-error" : {
|
||||
"used-in" : [ "src/app/main/data/media.cljs:81", "src/app/main/ui/auth/register.cljs:45", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/handoff/exports.cljs:41" ],
|
||||
"used-in" : [ "src/app/main/data/media.cljs:81", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:45", "src/app/main/ui/handoff/exports.cljs:41" ],
|
||||
"translations" : {
|
||||
"en" : "An unexpected error occurred.",
|
||||
"fr" : "Une erreur inattendue c'est produite",
|
||||
|
@ -986,21 +986,21 @@
|
|||
}
|
||||
},
|
||||
"handoff.attributes.image.download" : {
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:45" ],
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:61" ],
|
||||
"translations" : {
|
||||
"en" : "Dowload source image",
|
||||
"es" : "Descargar imagen original"
|
||||
}
|
||||
},
|
||||
"handoff.attributes.image.height" : {
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:37" ],
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:49" ],
|
||||
"translations" : {
|
||||
"en" : "Height",
|
||||
"es" : "Altura"
|
||||
}
|
||||
},
|
||||
"handoff.attributes.image.width" : {
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:32" ],
|
||||
"used-in" : [ "src/app/main/ui/handoff/attributes/image.cljs:44" ],
|
||||
"translations" : {
|
||||
"en" : "Width",
|
||||
"es" : "Ancho"
|
||||
|
@ -1368,7 +1368,7 @@
|
|||
"unused" : true
|
||||
},
|
||||
"labels.admin" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:85", "src/app/main/ui/dashboard/team.cljs:174", "src/app/main/ui/dashboard/team.cljs:190" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:85", "src/app/main/ui/dashboard/team.cljs:176", "src/app/main/ui/dashboard/team.cljs:192" ],
|
||||
"translations" : {
|
||||
"en" : "Admin",
|
||||
"es" : "Administración"
|
||||
|
@ -1381,6 +1381,18 @@
|
|||
"es" : "Todo"
|
||||
}
|
||||
},
|
||||
"labels.bad-gateway.desc-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:58" ],
|
||||
"translations" : {
|
||||
"en" : "Looks like you need to wait a bit and retry; we are performing small maintenance of our servers."
|
||||
}
|
||||
},
|
||||
"labels.bad-gateway.main-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:57" ],
|
||||
"translations" : {
|
||||
"en" : "Bad Gateway"
|
||||
}
|
||||
},
|
||||
"labels.cancel" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:201" ],
|
||||
"translations" : {
|
||||
|
@ -1414,7 +1426,7 @@
|
|||
}
|
||||
},
|
||||
"labels.delete" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/files.cljs:85", "src/app/main/ui/dashboard/grid.cljs:177" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:179", "src/app/main/ui/dashboard/files.cljs:85" ],
|
||||
"translations" : {
|
||||
"en" : "Delete",
|
||||
"fr" : "Supprimer",
|
||||
|
@ -1453,14 +1465,14 @@
|
|||
}
|
||||
},
|
||||
"labels.editor" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:86", "src/app/main/ui/dashboard/team.cljs:177", "src/app/main/ui/dashboard/team.cljs:191" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:86", "src/app/main/ui/dashboard/team.cljs:179", "src/app/main/ui/dashboard/team.cljs:193" ],
|
||||
"translations" : {
|
||||
"en" : "Editor",
|
||||
"es" : "Editor"
|
||||
}
|
||||
},
|
||||
"labels.email" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:112", "src/app/main/ui/dashboard/team.cljs:215" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:114", "src/app/main/ui/dashboard/team.cljs:217" ],
|
||||
"translations" : {
|
||||
"en" : "Email",
|
||||
"fr" : "Adresse email",
|
||||
|
@ -1478,7 +1490,7 @@
|
|||
}
|
||||
},
|
||||
"labels.hide-resolved-comments" : {
|
||||
"used-in" : [ "src/app/main/ui/viewer/header.cljs:175", "src/app/main/ui/workspace/comments.cljs:129" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/comments.cljs:129", "src/app/main/ui/viewer/header.cljs:175" ],
|
||||
"translations" : {
|
||||
"en" : "Hide resolved comments",
|
||||
"es" : "Ocultar comentarios resueltos"
|
||||
|
@ -1503,14 +1515,14 @@
|
|||
}
|
||||
},
|
||||
"labels.members" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:297", "src/app/main/ui/dashboard/team.cljs:60", "src/app/main/ui/dashboard/team.cljs:66" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:60", "src/app/main/ui/dashboard/team.cljs:66", "src/app/main/ui/dashboard/sidebar.cljs:297" ],
|
||||
"translations" : {
|
||||
"en" : "Members",
|
||||
"es" : "Integrantes"
|
||||
}
|
||||
},
|
||||
"labels.name" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:214" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:216" ],
|
||||
"translations" : {
|
||||
"en" : "Name",
|
||||
"fr" : "Nom",
|
||||
|
@ -1534,15 +1546,33 @@
|
|||
"es" : "No tienes notificaciones de comentarios pendientes"
|
||||
}
|
||||
},
|
||||
"labels.not-found.auth-info" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:42" ],
|
||||
"translations" : {
|
||||
"en" : "You’re signed in as"
|
||||
}
|
||||
},
|
||||
"labels.not-found.desc-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:40" ],
|
||||
"translations" : {
|
||||
"en" : "This page might not exist or you don’t have permissions to access to it."
|
||||
}
|
||||
},
|
||||
"labels.not-found.main-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:39" ],
|
||||
"translations" : {
|
||||
"en" : "Oops!"
|
||||
}
|
||||
},
|
||||
"labels.num-of-files" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:308" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:310" ],
|
||||
"translations" : {
|
||||
"en" : [ "1 file", "%s files" ],
|
||||
"es" : [ "1 archivo", "%s archivos" ]
|
||||
}
|
||||
},
|
||||
"labels.num-of-projects" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:305" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:307" ],
|
||||
"translations" : {
|
||||
"en" : [ "1 project", "%s projects" ],
|
||||
"es" : [ "1 proyecto", "%s proyectos" ]
|
||||
|
@ -1565,7 +1595,7 @@
|
|||
}
|
||||
},
|
||||
"labels.owner" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:171", "src/app/main/ui/dashboard/team.cljs:296" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:173", "src/app/main/ui/dashboard/team.cljs:298" ],
|
||||
"translations" : {
|
||||
"en" : "Owner",
|
||||
"es" : "Dueño"
|
||||
|
@ -1581,7 +1611,7 @@
|
|||
}
|
||||
},
|
||||
"labels.permissions" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:216" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:218" ],
|
||||
"translations" : {
|
||||
"en" : "Permissions",
|
||||
"es" : "Permisos"
|
||||
|
@ -1606,7 +1636,7 @@
|
|||
}
|
||||
},
|
||||
"labels.remove" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/libraries.cljs:92", "src/app/main/ui/dashboard/team.cljs:202" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/libraries.cljs:92", "src/app/main/ui/dashboard/team.cljs:204" ],
|
||||
"translations" : {
|
||||
"en" : "Remove",
|
||||
"fr" : "",
|
||||
|
@ -1615,12 +1645,18 @@
|
|||
}
|
||||
},
|
||||
"labels.rename" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:300", "src/app/main/ui/dashboard/files.cljs:84", "src/app/main/ui/dashboard/grid.cljs:176" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/grid.cljs:178", "src/app/main/ui/dashboard/sidebar.cljs:300", "src/app/main/ui/dashboard/files.cljs:84" ],
|
||||
"translations" : {
|
||||
"en" : "Rename",
|
||||
"es" : "Renombrar"
|
||||
}
|
||||
},
|
||||
"labels.retry" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:62", "src/app/main/ui/static.cljs:79" ],
|
||||
"translations" : {
|
||||
"en" : "Retry"
|
||||
}
|
||||
},
|
||||
"labels.role" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:84" ],
|
||||
"translations" : {
|
||||
|
@ -1628,8 +1664,20 @@
|
|||
"es" : "Cargo"
|
||||
}
|
||||
},
|
||||
"labels.service-unavailable.desc-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:75" ],
|
||||
"translations" : {
|
||||
"en" : "We are in programmed maintenance of our systems."
|
||||
}
|
||||
},
|
||||
"labels.service-unavailable.main-message" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:74" ],
|
||||
"translations" : {
|
||||
"en" : "Service Unavailable"
|
||||
}
|
||||
},
|
||||
"labels.settings" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/sidebar.cljs:298", "src/app/main/ui/dashboard/team.cljs:61", "src/app/main/ui/dashboard/team.cljs:68" ],
|
||||
"used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/team.cljs:61", "src/app/main/ui/dashboard/team.cljs:68", "src/app/main/ui/dashboard/sidebar.cljs:298" ],
|
||||
"translations" : {
|
||||
"en" : "Settings",
|
||||
"fr" : "Settings",
|
||||
|
@ -1647,19 +1695,28 @@
|
|||
}
|
||||
},
|
||||
"labels.show-all-comments" : {
|
||||
"used-in" : [ "src/app/main/ui/viewer/header.cljs:163", "src/app/main/ui/workspace/comments.cljs:117" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/comments.cljs:117", "src/app/main/ui/viewer/header.cljs:163" ],
|
||||
"translations" : {
|
||||
"en" : "Show all comments",
|
||||
"es" : "Mostrar todos los comentarios"
|
||||
}
|
||||
},
|
||||
"labels.show-your-comments" : {
|
||||
"used-in" : [ "src/app/main/ui/viewer/header.cljs:168", "src/app/main/ui/workspace/comments.cljs:122" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/comments.cljs:122", "src/app/main/ui/viewer/header.cljs:168" ],
|
||||
"translations" : {
|
||||
"en" : "Show only yours comments",
|
||||
"es" : "Mostrar sólo tus comentarios"
|
||||
}
|
||||
},
|
||||
"labels.sign-out" : {
|
||||
"used-in" : [ "src/app/main/ui/static.cljs:45" ],
|
||||
"translations" : {
|
||||
"en" : "Sign out",
|
||||
"fr" : "Quitter",
|
||||
"ru" : "Выход",
|
||||
"es" : "Salir"
|
||||
}
|
||||
},
|
||||
"labels.update" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/profile.cljs:104" ],
|
||||
"translations" : {
|
||||
|
@ -1670,7 +1727,7 @@
|
|||
}
|
||||
},
|
||||
"labels.viewer" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:87", "src/app/main/ui/dashboard/team.cljs:180", "src/app/main/ui/dashboard/team.cljs:192" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:87", "src/app/main/ui/dashboard/team.cljs:182", "src/app/main/ui/dashboard/team.cljs:194" ],
|
||||
"translations" : {
|
||||
"en" : "Viewer",
|
||||
"es" : "Visualizador"
|
||||
|
@ -1684,7 +1741,7 @@
|
|||
}
|
||||
},
|
||||
"media.loading" : {
|
||||
"used-in" : [ "src/app/main/data/media.cljs:60", "src/app/main/data/workspace/persistence.cljs:472", "src/app/main/data/workspace/persistence.cljs:527" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/persistence.cljs:459", "src/app/main/data/workspace/persistence.cljs:514", "src/app/main/data/media.cljs:60" ],
|
||||
"translations" : {
|
||||
"en" : "Loading image...",
|
||||
"fr" : "Chargement de l'image...",
|
||||
|
@ -1852,13 +1909,13 @@
|
|||
}
|
||||
},
|
||||
"modals.delete-page.body" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:45" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:60" ],
|
||||
"translations" : {
|
||||
"en" : "Are you sure you want to delete this page?"
|
||||
}
|
||||
},
|
||||
"modals.delete-page.title" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:44" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:59" ],
|
||||
"translations" : {
|
||||
"en" : "Delete page"
|
||||
}
|
||||
|
@ -1906,28 +1963,28 @@
|
|||
}
|
||||
},
|
||||
"modals.delete-team-member-confirm.accept" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:160" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:162" ],
|
||||
"translations" : {
|
||||
"en" : "Delete member",
|
||||
"es" : "Eliminando miembro"
|
||||
}
|
||||
},
|
||||
"modals.delete-team-member-confirm.message" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:159" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:161" ],
|
||||
"translations" : {
|
||||
"en" : "Are you sure you want to delete this member from the team?",
|
||||
"es" : "¿Seguro que quieres eliminar este integrante del equipo?"
|
||||
}
|
||||
},
|
||||
"modals.delete-team-member-confirm.title" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:158" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:160" ],
|
||||
"translations" : {
|
||||
"en" : "Delete team member",
|
||||
"es" : "Eliminar integrante del equipo"
|
||||
}
|
||||
},
|
||||
"modals.invite-member.title" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:108" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:110" ],
|
||||
"translations" : {
|
||||
"en" : "Invite to join the team",
|
||||
"es" : "Invitar a unirse al equipo"
|
||||
|
@ -1990,21 +2047,21 @@
|
|||
}
|
||||
},
|
||||
"modals.promote-owner-confirm.accept" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:147" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:149" ],
|
||||
"translations" : {
|
||||
"en" : "Promote",
|
||||
"es" : "Promocionar"
|
||||
}
|
||||
},
|
||||
"modals.promote-owner-confirm.message" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:146" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:148" ],
|
||||
"translations" : {
|
||||
"en" : "Are you sure you want to promote this user to owner?",
|
||||
"es" : "¿Seguro que quieres promocionar este usuario a dueño?"
|
||||
}
|
||||
},
|
||||
"modals.promote-owner-confirm.title" : {
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:145" ],
|
||||
"used-in" : [ "src/app/main/ui/dashboard/team.cljs:147" ],
|
||||
"translations" : {
|
||||
"en" : "Promote to owner",
|
||||
"es" : "Promocionar a dueño"
|
||||
|
@ -2047,7 +2104,7 @@
|
|||
}
|
||||
},
|
||||
"notifications.profile-saved" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/options.cljs:36", "src/app/main/ui/settings/profile.cljs:38" ],
|
||||
"used-in" : [ "src/app/main/ui/settings/profile.cljs:38", "src/app/main/ui/settings/options.cljs:36" ],
|
||||
"translations" : {
|
||||
"en" : "Profile saved successfully!",
|
||||
"fr" : "Profil enregistré avec succès!",
|
||||
|
@ -2056,7 +2113,7 @@
|
|||
}
|
||||
},
|
||||
"notifications.validation-email-sent" : {
|
||||
"used-in" : [ "src/app/main/ui/settings/change_email.cljs:56", "src/app/main/ui/auth/register.cljs:54" ],
|
||||
"used-in" : [ "src/app/main/ui/auth/register.cljs:54", "src/app/main/ui/settings/change_email.cljs:56" ],
|
||||
"translations" : {
|
||||
"en" : "Verification email sent to %s. Check your email!",
|
||||
"es" : "Verificación de email enviada a %s. Comprueba tu correo."
|
||||
|
@ -2072,7 +2129,7 @@
|
|||
}
|
||||
},
|
||||
"settings.multiple" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/shadow.cljs:213", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:161", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:170", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162", "src/app/main/ui/workspace/sidebar/options/blur.cljs:79", "src/app/main/ui/workspace/sidebar/options/stroke.cljs:154" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:154", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:161", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:170", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162", "src/app/main/ui/workspace/sidebar/options/shadow.cljs:213", "src/app/main/ui/workspace/sidebar/options/blur.cljs:79" ],
|
||||
"translations" : {
|
||||
"en" : "Mixed",
|
||||
"fr" : null,
|
||||
|
@ -2351,7 +2408,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.delete" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:140", "src/app/main/ui/workspace/sidebar/assets.cljs:260", "src/app/main/ui/workspace/sidebar/assets.cljs:375", "src/app/main/ui/workspace/sidebar/assets.cljs:504" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:151", "src/app/main/ui/workspace/sidebar/assets.cljs:140", "src/app/main/ui/workspace/sidebar/assets.cljs:260", "src/app/main/ui/workspace/sidebar/assets.cljs:375", "src/app/main/ui/workspace/sidebar/assets.cljs:504" ],
|
||||
"translations" : {
|
||||
"en" : "Delete",
|
||||
"fr" : "",
|
||||
|
@ -2360,7 +2417,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.duplicate" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:139" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:155", "src/app/main/ui/workspace/sidebar/assets.cljs:139" ],
|
||||
"translations" : {
|
||||
"en" : "Duplicate",
|
||||
"fr" : "",
|
||||
|
@ -2414,7 +2471,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.assets.rename" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:138", "src/app/main/ui/workspace/sidebar/assets.cljs:259", "src/app/main/ui/workspace/sidebar/assets.cljs:373", "src/app/main/ui/workspace/sidebar/assets.cljs:502" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:154", "src/app/main/ui/workspace/sidebar/assets.cljs:138", "src/app/main/ui/workspace/sidebar/assets.cljs:259", "src/app/main/ui/workspace/sidebar/assets.cljs:373", "src/app/main/ui/workspace/sidebar/assets.cljs:502" ],
|
||||
"translations" : {
|
||||
"en" : "Rename",
|
||||
"fr" : "",
|
||||
|
@ -2502,14 +2559,14 @@
|
|||
}
|
||||
},
|
||||
"workspace.gradients.linear" : {
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:43", "src/app/main/ui/components/color_bullet.cljs:31" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:72", "src/app/main/ui/components/color_bullet.cljs:31" ],
|
||||
"translations" : {
|
||||
"en" : "Linear gradient",
|
||||
"es" : "Degradado lineal"
|
||||
}
|
||||
},
|
||||
"workspace.gradients.radial" : {
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:44", "src/app/main/ui/components/color_bullet.cljs:32" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:73", "src/app/main/ui/components/color_bullet.cljs:32" ],
|
||||
"translations" : {
|
||||
"en" : "Radial gradient",
|
||||
"es" : "Degradado radial"
|
||||
|
@ -2706,21 +2763,21 @@
|
|||
}
|
||||
},
|
||||
"workspace.libraries.colors.big-thumbnails" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:170" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:171" ],
|
||||
"translations" : {
|
||||
"en" : "Big thumbnails",
|
||||
"es" : "Miniaturas grandes"
|
||||
}
|
||||
},
|
||||
"workspace.libraries.colors.file-library" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:89", "src/app/main/ui/workspace/colorpalette.cljs:148" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:89", "src/app/main/ui/workspace/colorpalette.cljs:149" ],
|
||||
"translations" : {
|
||||
"en" : "File library",
|
||||
"es" : "Biblioteca del archivo"
|
||||
}
|
||||
},
|
||||
"workspace.libraries.colors.recent-colors" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:88", "src/app/main/ui/workspace/colorpalette.cljs:158" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:88", "src/app/main/ui/workspace/colorpalette.cljs:159" ],
|
||||
"translations" : {
|
||||
"en" : "Recent colors",
|
||||
"es" : "Colores recientes"
|
||||
|
@ -2734,7 +2791,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.libraries.colors.small-thumbnails" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:175" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/colorpalette.cljs:176" ],
|
||||
"translations" : {
|
||||
"en" : "Small thumbnails",
|
||||
"es" : "Miniaturas pequeñas"
|
||||
|
@ -3251,7 +3308,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.options.position" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:118", "src/app/main/ui/workspace/sidebar/options/measures.cljs:146" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:146", "src/app/main/ui/workspace/sidebar/options/frame.cljs:118" ],
|
||||
"translations" : {
|
||||
"en" : "Position",
|
||||
"fr" : "Position",
|
||||
|
@ -3384,7 +3441,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.options.size" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:93", "src/app/main/ui/workspace/sidebar/options/measures.cljs:118" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:118", "src/app/main/ui/workspace/sidebar/options/frame.cljs:93" ],
|
||||
"translations" : {
|
||||
"en" : "Size",
|
||||
"fr" : "Taille",
|
||||
|
@ -3594,7 +3651,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.options.text-options.none" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/typography.cljs:176", "src/app/main/ui/workspace/sidebar/options/text.cljs:153" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/text.cljs:153", "src/app/main/ui/workspace/sidebar/options/typography.cljs:176" ],
|
||||
"translations" : {
|
||||
"en" : "None",
|
||||
"fr" : "Aucune",
|
||||
|
@ -3733,7 +3790,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.shape.menu.detach-instance" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/context_menu.cljs:163", "src/app/main/ui/workspace/context_menu.cljs:173", "src/app/main/ui/workspace/sidebar/options/component.cljs:79", "src/app/main/ui/workspace/sidebar/options/component.cljs:84" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/component.cljs:79", "src/app/main/ui/workspace/sidebar/options/component.cljs:84", "src/app/main/ui/workspace/context_menu.cljs:163", "src/app/main/ui/workspace/context_menu.cljs:173" ],
|
||||
"translations" : {
|
||||
"en" : "Detach instance",
|
||||
"es" : "Desacoplar instancia"
|
||||
|
@ -3761,7 +3818,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.shape.menu.go-master" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/context_menu.cljs:177", "src/app/main/ui/workspace/sidebar/options/component.cljs:86" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/component.cljs:86", "src/app/main/ui/workspace/context_menu.cljs:177" ],
|
||||
"translations" : {
|
||||
"en" : "Go to master component file",
|
||||
"es" : "Ir al archivo del componente maestro"
|
||||
|
@ -3803,7 +3860,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.shape.menu.reset-overrides" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/context_menu.cljs:165", "src/app/main/ui/workspace/context_menu.cljs:175", "src/app/main/ui/workspace/sidebar/options/component.cljs:80", "src/app/main/ui/workspace/sidebar/options/component.cljs:85" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/component.cljs:80", "src/app/main/ui/workspace/sidebar/options/component.cljs:85", "src/app/main/ui/workspace/context_menu.cljs:165", "src/app/main/ui/workspace/context_menu.cljs:175" ],
|
||||
"translations" : {
|
||||
"en" : "Reset overrides",
|
||||
"es" : "Deshacer modificaciones"
|
||||
|
@ -3817,7 +3874,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.shape.menu.show-master" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/context_menu.cljs:169", "src/app/main/ui/workspace/sidebar/options/component.cljs:82" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/component.cljs:82", "src/app/main/ui/workspace/context_menu.cljs:169" ],
|
||||
"translations" : {
|
||||
"en" : "Show master component",
|
||||
"es" : "Ver componente maestro"
|
||||
|
@ -3845,7 +3902,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.shape.menu.update-master" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/context_menu.cljs:167", "src/app/main/ui/workspace/sidebar/options/component.cljs:81" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/options/component.cljs:81", "src/app/main/ui/workspace/context_menu.cljs:167" ],
|
||||
"translations" : {
|
||||
"en" : "Update master component",
|
||||
"es" : "Actualizar componente maestro"
|
||||
|
@ -3861,7 +3918,7 @@
|
|||
"unused" : true
|
||||
},
|
||||
"workspace.sidebar.sitemap" : {
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:171" ],
|
||||
"used-in" : [ "src/app/main/ui/workspace/sidebar/sitemap.cljs:207" ],
|
||||
"translations" : {
|
||||
"en" : "Pages",
|
||||
"fr" : "Pages",
|
||||
|
@ -4244,7 +4301,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.updates.dismiss" : {
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:649" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:697" ],
|
||||
"translations" : {
|
||||
"en" : "Dismiss",
|
||||
"fr" : "",
|
||||
|
@ -4253,7 +4310,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.updates.there-are-updates" : {
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:645" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:693" ],
|
||||
"translations" : {
|
||||
"en" : "There are updates in shared libraries",
|
||||
"fr" : "",
|
||||
|
@ -4262,7 +4319,7 @@
|
|||
}
|
||||
},
|
||||
"workspace.updates.update" : {
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:647" ],
|
||||
"used-in" : [ "src/app/main/data/workspace/libraries.cljs:695" ],
|
||||
"translations" : {
|
||||
"en" : "Update",
|
||||
"fr" : "",
|
||||
|
|
|
@ -12,22 +12,22 @@
|
|||
//
|
||||
//#################################################
|
||||
|
||||
@import 'common/dependencies/colors';
|
||||
@import 'common/dependencies/helpers';
|
||||
@import 'common/dependencies/mixin';
|
||||
@import 'common/dependencies/fonts';
|
||||
@import 'common/dependencies/reset';
|
||||
@import 'common/dependencies/animations';
|
||||
@import 'common/dependencies/z-index';
|
||||
@import 'common/dependencies/highlightjs-theme';
|
||||
@import "common/dependencies/colors";
|
||||
@import "common/dependencies/helpers";
|
||||
@import "common/dependencies/mixin";
|
||||
@import "common/dependencies/fonts";
|
||||
@import "common/dependencies/reset";
|
||||
@import "common/dependencies/animations";
|
||||
@import "common/dependencies/z-index";
|
||||
@import "common/dependencies/highlightjs-theme";
|
||||
|
||||
//#################################################
|
||||
// Layouts
|
||||
//#################################################
|
||||
|
||||
@import 'common/base';
|
||||
@import 'main/layouts/login';
|
||||
@import 'main/layouts/main-layout';
|
||||
@import "common/base";
|
||||
@import "main/layouts/login";
|
||||
@import "main/layouts/main-layout";
|
||||
@import "main/layouts/not-found";
|
||||
@import "main/layouts/viewer";
|
||||
@import "main/layouts/handoff";
|
||||
|
@ -36,12 +36,12 @@
|
|||
// Commons
|
||||
//#################################################
|
||||
|
||||
@import 'common/framework';
|
||||
@import 'main/partials/modal';
|
||||
@import 'main/partials/forms';
|
||||
@import "common/framework";
|
||||
@import "main/partials/modal";
|
||||
@import "main/partials/forms";
|
||||
@import "main/partials/texts";
|
||||
@import 'main/partials/context-menu';
|
||||
@import 'main/partials/dropdown';
|
||||
@import "main/partials/context-menu";
|
||||
@import "main/partials/dropdown";
|
||||
|
||||
//#################################################
|
||||
// Partials
|
||||
|
@ -51,35 +51,36 @@
|
|||
@import "main/partials/viewer-header";
|
||||
@import "main/partials/viewer-thumbnails";
|
||||
@import "main/partials/zoom-widget";
|
||||
@import 'main/partials/activity-bar';
|
||||
@import 'main/partials/color-palette';
|
||||
@import 'main/partials/colorpicker';
|
||||
@import 'main/partials/dashboard';
|
||||
@import 'main/partials/dashboard-header';
|
||||
@import 'main/partials/dashboard-grid';
|
||||
@import 'main/partials/dashboard-sidebar';
|
||||
@import 'main/partials/dashboard-team';
|
||||
@import 'main/partials/dashboard-settings';
|
||||
@import 'main/partials/debug-icons-preview';
|
||||
@import 'main/partials/editable-label';
|
||||
@import 'main/partials/left-toolbar';
|
||||
@import 'main/partials/loader';
|
||||
@import 'main/partials/project-bar';
|
||||
@import 'main/partials/sidebar';
|
||||
@import 'main/partials/sidebar-align-options';
|
||||
@import 'main/partials/sidebar-assets';
|
||||
@import 'main/partials/sidebar-document-history';
|
||||
@import 'main/partials/sidebar-element-options';
|
||||
@import 'main/partials/sidebar-icons';
|
||||
@import 'main/partials/sidebar-interactions';
|
||||
@import 'main/partials/sidebar-layers';
|
||||
@import 'main/partials/sidebar-sitemap';
|
||||
@import 'main/partials/sidebar-tools';
|
||||
@import 'main/partials/tab-container';
|
||||
@import 'main/partials/tool-bar';
|
||||
@import 'main/partials/user-settings';
|
||||
@import 'main/partials/workspace';
|
||||
@import 'main/partials/workspace-header';
|
||||
@import 'main/partials/comments';
|
||||
@import 'main/partials/color-bullet';
|
||||
@import "main/partials/activity-bar";
|
||||
@import "main/partials/color-palette";
|
||||
@import "main/partials/colorpicker";
|
||||
@import "main/partials/dashboard";
|
||||
@import "main/partials/dashboard-header";
|
||||
@import "main/partials/dashboard-grid";
|
||||
@import "main/partials/dashboard-sidebar";
|
||||
@import "main/partials/dashboard-team";
|
||||
@import "main/partials/dashboard-settings";
|
||||
@import "main/partials/debug-icons-preview";
|
||||
@import "main/partials/editable-label";
|
||||
@import "main/partials/left-toolbar";
|
||||
@import "main/partials/loader";
|
||||
@import "main/partials/project-bar";
|
||||
@import "main/partials/sidebar";
|
||||
@import "main/partials/sidebar-align-options";
|
||||
@import "main/partials/sidebar-assets";
|
||||
@import "main/partials/sidebar-document-history";
|
||||
@import "main/partials/sidebar-element-options";
|
||||
@import "main/partials/sidebar-icons";
|
||||
@import "main/partials/sidebar-interactions";
|
||||
@import "main/partials/sidebar-layers";
|
||||
@import "main/partials/sidebar-sitemap";
|
||||
@import "main/partials/sidebar-tools";
|
||||
@import "main/partials/tab-container";
|
||||
@import "main/partials/tool-bar";
|
||||
@import "main/partials/user-settings";
|
||||
@import "main/partials/workspace";
|
||||
@import "main/partials/workspace-header";
|
||||
@import "main/partials/comments";
|
||||
@import "main/partials/color-bullet";
|
||||
@import "main/partials/handoff";
|
||||
@import "main/partials/exception-page";
|
||||
|
|
|
@ -29,34 +29,55 @@
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.error-img {
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.image {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
svg {
|
||||
height: 320px;
|
||||
width: 200px;
|
||||
height: 220px;
|
||||
width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-message {
|
||||
color: $color-black;
|
||||
font-size: 11rem;
|
||||
line-height: 200px;
|
||||
font-size: 5rem;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.desc-message {
|
||||
color: $color-black;
|
||||
font-size: 2.2rem;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin-top: $x-big;
|
||||
.sign-info {
|
||||
margin-top: 20px;
|
||||
color: $color-black;
|
||||
font-size: 1rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
b {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
83
frontend/resources/styles/main/partials/exception-page.scss
Normal file
83
frontend/resources/styles/main/partials/exception-page.scss
Normal file
|
@ -0,0 +1,83 @@
|
|||
.exception-layout {
|
||||
display: grid;
|
||||
|
||||
grid-template-rows: 120px auto;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.exception-header {
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 1 / span 1;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32px;
|
||||
|
||||
svg {
|
||||
height: 55px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.exception-content {
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 1 / span 2;
|
||||
height: 100vh;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.image {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
svg {
|
||||
height: 220px;
|
||||
width: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-message {
|
||||
color: $color-black;
|
||||
font-size: 5rem;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.desc-message {
|
||||
color: $color-black;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sign-info {
|
||||
margin-top: 20px;
|
||||
color: $color-black;
|
||||
font-size: 1rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
b {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
(rt/initialize-history on-navigate))
|
||||
|
||||
(st/emit! udu/fetch-profile)
|
||||
(mf/mount (mf/element ui/app-wrapper) (dom/get-element "app"))
|
||||
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
||||
(mf/mount (mf/element modal) (dom/get-element "modal")))
|
||||
|
||||
(defn ^:export init
|
||||
|
|
|
@ -129,3 +129,13 @@
|
|||
:actions actions
|
||||
:tag tag})))
|
||||
|
||||
(defn assign-exception
|
||||
[{:keys [type] :as error}]
|
||||
(us/assert (s/nilable map?) error)
|
||||
(us/assert (s/nilable ::us/keyword) type)
|
||||
(ptk/reify ::assign-exception
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (nil? error)
|
||||
(dissoc state :exception)
|
||||
(assoc state :exception error)))))
|
||||
|
|
|
@ -130,13 +130,11 @@
|
|||
(rx/map #(shapes-changes-persisted file-id %))))))
|
||||
|
||||
on-error
|
||||
(fn [{:keys [type status] :as error}]
|
||||
(if (and (= :server-error type)
|
||||
(= 502 status))
|
||||
(fn [{:keys [type] :as error}]
|
||||
(if (or (= :bad-gateway type)
|
||||
(= :service-unavailable type))
|
||||
(rx/of (update-persistence-status {:status :error :reason type}))
|
||||
(rx/of update-persistence-queue
|
||||
(update-persistence-status {:status :error :reason type}))))]
|
||||
|
||||
(rx/throw error)))]
|
||||
|
||||
(when (= file-id (:id file))
|
||||
(->> (rp/mutation :update-file params)
|
||||
|
@ -219,18 +217,7 @@
|
|||
(rp/query :project {:id project-id})
|
||||
(rp/query :file-libraries {:file-id file-id}))
|
||||
(rx/first)
|
||||
(rx/map (fn [bundle] (apply bundle-fetched bundle)))
|
||||
(rx/catch (fn [{:keys [type code] :as error}]
|
||||
(cond
|
||||
(= :not-found type)
|
||||
(rx/of (rt/nav' :not-found))
|
||||
|
||||
(and (= :authentication type)
|
||||
(= :unauthorized code))
|
||||
(rx/of (rt/nav' :not-authorized))
|
||||
|
||||
:else
|
||||
(throw error))))))))
|
||||
(rx/map (fn [bundle] (apply bundle-fetched bundle)))))))
|
||||
|
||||
(defn- bundle-fetched
|
||||
[file users project libraries]
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
(def profile
|
||||
(l/derived :profile st/state))
|
||||
|
||||
(def exception
|
||||
(l/derived :exception st/state))
|
||||
|
||||
;; ---- Dashboard refs
|
||||
|
||||
(def dashboard-local
|
||||
|
|
|
@ -15,34 +15,32 @@
|
|||
[app.util.http-api :as http]))
|
||||
|
||||
(defn- handle-response
|
||||
[response]
|
||||
[{:keys [status body] :as response}]
|
||||
(cond
|
||||
(http/success? response)
|
||||
(rx/of (:body response))
|
||||
(= 204 status)
|
||||
(rx/empty)
|
||||
|
||||
(= (:status response) 400)
|
||||
(rx/throw (:body response))
|
||||
(= 502 status)
|
||||
(rx/throw {:type :bad-gateway})
|
||||
|
||||
(= (:status response) 401)
|
||||
(rx/throw {:type :authentication
|
||||
:code :not-authenticated})
|
||||
|
||||
(= (:status response) 403)
|
||||
(rx/throw {:type :authorization
|
||||
:code :not-authorized})
|
||||
|
||||
(= (:status response) 404)
|
||||
(rx/throw (:body response))
|
||||
(= 503 status)
|
||||
(rx/throw {:type :service-unavailable})
|
||||
|
||||
(= 0 (:status response))
|
||||
(rx/throw {:type :offline})
|
||||
|
||||
(and (= 200 status)
|
||||
(coll? body))
|
||||
(rx/of body)
|
||||
|
||||
(and (>= status 400)
|
||||
(map? body))
|
||||
(rx/throw body)
|
||||
|
||||
:else
|
||||
(rx/throw (merge {:type :server-error
|
||||
:status (:status response)}
|
||||
(:body response)))))
|
||||
|
||||
|
||||
(rx/throw {:type :unexpected-error
|
||||
:status status
|
||||
:data body})))
|
||||
|
||||
(defn send-query!
|
||||
[id params]
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
(defonce state (ptk/store {:resolve ptk/resolve}))
|
||||
(defonce stream (ptk/input-stream state))
|
||||
|
||||
(defn ^boolean is-logged?
|
||||
[pdata]
|
||||
(and (some? pdata)
|
||||
(uuid? (:id pdata))
|
||||
(not= uuid/zero (:id pdata))))
|
||||
|
||||
(when *assert*
|
||||
(defonce debug-subscription
|
||||
(->> stream
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
[app.main.ui.messages :as msgs]
|
||||
[app.main.ui.render :as render]
|
||||
[app.main.ui.settings :as settings]
|
||||
[app.main.ui.static :refer [not-found-page not-authorized-page]]
|
||||
[app.main.ui.static :as static]
|
||||
[app.main.ui.viewer :refer [viewer-page]]
|
||||
[app.main.ui.handoff :refer [handoff]]
|
||||
[app.main.ui.workspace :as workspace]
|
||||
|
@ -37,6 +37,7 @@
|
|||
[app.util.router :as rt]
|
||||
[cuerdas.core :as str]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cljs.pprint :refer [pprint]]
|
||||
[expound.alpha :as expound]
|
||||
[potok.core :as ptk]
|
||||
[rumext.alpha :as mf]))
|
||||
|
@ -81,9 +82,6 @@
|
|||
:conform {:path-params ::viewer-path-params
|
||||
:query-params ::viewer-query-params}}]
|
||||
|
||||
["/not-found" :not-found]
|
||||
["/not-authorized" :not-authorized]
|
||||
|
||||
(when *assert*
|
||||
["/debug/icons-preview" :debug-icons-preview])
|
||||
|
||||
|
@ -100,19 +98,15 @@
|
|||
|
||||
["/workspace/:project-id/:file-id" :workspace]])
|
||||
|
||||
(mf/defc app-error
|
||||
(mf/defc on-main-error
|
||||
[{:keys [error] :as props}]
|
||||
(let [data (ex-data error)]
|
||||
(case (:type data)
|
||||
:not-found [:& not-found-page {:error data}]
|
||||
(do
|
||||
(ptk/handle-error error)
|
||||
[:span "Internal application errror"]))))
|
||||
(ptk/handle-error error)
|
||||
[:span "Internal application errror"]))
|
||||
|
||||
(mf/defc app
|
||||
{::mf/wrap [#(mf/catch % {:fallback app-error})]}
|
||||
(mf/defc main-page
|
||||
{::mf/wrap [#(mf/catch % {:fallback on-main-error})]}
|
||||
[{:keys [route] :as props}]
|
||||
|
||||
[:& (mf/provider ctx/current-route) {:value route}
|
||||
(case (get-in route [:data :name])
|
||||
(:auth-login
|
||||
|
@ -189,67 +183,71 @@
|
|||
:page-id page-id
|
||||
:layout-name (keyword layout-name)
|
||||
:key file-id}])
|
||||
|
||||
:not-authorized
|
||||
[:& not-authorized-page]
|
||||
|
||||
:not-found
|
||||
[:& not-found-page]
|
||||
|
||||
nil)])
|
||||
|
||||
(mf/defc app-wrapper
|
||||
(mf/defc app
|
||||
[]
|
||||
(let [route (mf/deref refs/route)]
|
||||
[:*
|
||||
[:& msgs/notifications]
|
||||
(when route
|
||||
[:& app {:route route}])]))
|
||||
(let [route (mf/deref refs/route)
|
||||
edata (mf/deref refs/exception)]
|
||||
[:& (mf/provider ctx/current-route) {:value route}
|
||||
(if edata
|
||||
[:& static/exception-page {:data edata}]
|
||||
[:*
|
||||
[:& msgs/notifications]
|
||||
(when route
|
||||
[:& main-page {:route route}])])]))
|
||||
|
||||
;; --- Error Handling
|
||||
|
||||
;; That are special case server-errors that should be treated
|
||||
;; differently.
|
||||
(derive :not-found ::exceptional-state)
|
||||
(derive :bad-gateway ::exceptional-state)
|
||||
(derive :service-unavailable ::exceptional-state)
|
||||
|
||||
(defmethod ptk/handle-error ::exceptional-state
|
||||
[{:keys [status] :as error}]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/assign-exception error))))
|
||||
|
||||
;; We receive a explicit authentication error; this explicitly clears
|
||||
;; all profile data and redirect the user to the login page.
|
||||
(defmethod ptk/handle-error :authentication
|
||||
[error]
|
||||
(ts/schedule (st/emitf (logout))))
|
||||
|
||||
;; Error that happens on an active bussines model validation does not
|
||||
;; passes an validation (example: profile can't leave a team). From
|
||||
;; the user perspective a error flash message should be visualized but
|
||||
;; user can continue operate on the application.
|
||||
(defmethod ptk/handle-error :validation
|
||||
[error]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Unexpected validation error (server side)."
|
||||
:type :error
|
||||
:timeout 5000})))
|
||||
(when-let [explain (:hint-verbose error)]
|
||||
(js/console.group "Server Error")
|
||||
(js/console.error (if (map? error) (pr-str error) error))
|
||||
(js/console.error explain)
|
||||
(js/console.endGroup "Server Error")))
|
||||
(st/emitf
|
||||
(dm/show {:content "Unexpected validation error (server side)."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
|
||||
(defmethod ptk/handle-error :spec-validation
|
||||
[error]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Unexpected validation error (server side)."
|
||||
:type :error
|
||||
:timeout 5000})))
|
||||
;; Print to the console some debug info.
|
||||
(js/console.group "Server Error")
|
||||
(js/console.info
|
||||
(with-out-str
|
||||
(pprint (dissoc error :explain))))
|
||||
(when-let [explain (:explain error)]
|
||||
(js/console.group "Server Error")
|
||||
(js/console.error (if (map? error) (pr-str error) error))
|
||||
(js/console.error explain)
|
||||
(js/console.endGroup "Server Error")))
|
||||
|
||||
|
||||
(defmethod ptk/handle-error :authentication
|
||||
[error]
|
||||
(ts/schedule 0 #(st/emit! (logout))))
|
||||
|
||||
(defmethod ptk/handle-error :authorization
|
||||
[error]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Not authorized to see this content."
|
||||
:timeout 2000
|
||||
:type :error}))))
|
||||
(js/console.error explain))
|
||||
(js/console.endGroup "Server Error"))
|
||||
|
||||
;; This is a pure frontend error that can be caused by an active
|
||||
;; assertion (assertion that is preserved on production builds). From
|
||||
;; the user perspective this should be treated as internal error.
|
||||
(defmethod ptk/handle-error :assertion
|
||||
[{:keys [data stack message context] :as error}]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Internal assertion error."
|
||||
(st/emitf (dm/show {:content "Internal error: assertion."
|
||||
:type :error
|
||||
:timeout 2000})))
|
||||
:timeout 3000})))
|
||||
|
||||
;; Print to the console some debugging info
|
||||
(js/console.group message)
|
||||
(js/console.info (str/format "ns: '%s'\nname: '%s'\nfile: '%s:%s'"
|
||||
(:ns context)
|
||||
|
@ -259,48 +257,49 @@
|
|||
(js/console.groupCollapsed "Stack Trace")
|
||||
(js/console.info stack)
|
||||
(js/console.groupEnd "Stack Trace")
|
||||
|
||||
(js/console.error (with-out-str (expound/printer data)))
|
||||
(js/console.groupEnd message))
|
||||
|
||||
;; This happens when the backed server fails to process the
|
||||
;; request. This can be caused by an internal assertion or any other
|
||||
;; uncontrolled error.
|
||||
(defmethod ptk/handle-error :server-error
|
||||
[{:keys [data] :as error}]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show
|
||||
{:content "Something wrong has happened (on backend)."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
(js/console.group "Internal Server Error:")
|
||||
(js/console.error "hint:" (or (:hint data) (:message data)))
|
||||
(js/console.info
|
||||
(with-out-str
|
||||
(pprint (dissoc data :explain))))
|
||||
(when-let [explain (:explain data)]
|
||||
(js/console.error explain))
|
||||
(js/console.groupEnd "Internal Server Error:"))
|
||||
|
||||
(defmethod ptk/handle-error :default
|
||||
[error]
|
||||
(if (instance? ExceptionInfo error)
|
||||
(ptk/handle-error (ex-data error))
|
||||
(do
|
||||
(js/console.group "Generic Error")
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show
|
||||
{:content "Something wrong has happened."
|
||||
:type :error
|
||||
:timeout 3000})))
|
||||
|
||||
(js/console.group "Internal error:")
|
||||
(js/console.log "hint:" (or (ex-message error)
|
||||
(:hint error)
|
||||
(:message error)))
|
||||
(ex/ignoring
|
||||
(js/console.error "repr: " (pr-str error))
|
||||
(js/console.error "data: " (clj->js error))
|
||||
(js/console.error "stack:" (.-stack error)))
|
||||
(js/console.groupEnd "Generic error")
|
||||
(ts/schedule (st/emitf (dm/show
|
||||
{:content "Something wrong has happened."
|
||||
:type :error
|
||||
:timeout 3000}))))))
|
||||
(js/console.groupEnd "Internal error:"))))
|
||||
|
||||
(defmethod ptk/handle-error :server-error
|
||||
[{:keys [status] :as error}]
|
||||
(cond
|
||||
(= status 429)
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Too many requests, wait a little bit and retry."
|
||||
:type :error
|
||||
:timeout 5000})))
|
||||
|
||||
:else
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Unable to connect to backend, wait a little bit and refresh."
|
||||
:type :error})))))
|
||||
|
||||
|
||||
(defmethod ptk/handle-error :not-found
|
||||
[{:keys [status] :as error}]
|
||||
(ts/schedule
|
||||
(st/emitf (dm/show {:content "Resource not found."
|
||||
:type :warning}))))
|
||||
|
||||
(defonce uncaught-error-handler
|
||||
(letfn [(on-error [event]
|
||||
|
|
|
@ -11,27 +11,104 @@
|
|||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.data.auth :as da]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.store :as st]
|
||||
[app.main.refs :as refs]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.main.ui.icons :as i]))
|
||||
|
||||
(mf/defc not-found-page
|
||||
[{:keys [error] :as props}]
|
||||
[:section.not-found-layout
|
||||
[:div.not-found-header i/logo]
|
||||
[:div.not-found-content
|
||||
[:div.message-container
|
||||
[:div.error-img i/icon-empty]
|
||||
[:div.main-message "404"]
|
||||
[:div.desc-message "Oops! Page not found"]
|
||||
[:a.btn-primary.btn-small "Go back"]]]])
|
||||
(defn- go-to-dashboard
|
||||
[profile]
|
||||
(let [team-id (:default-team-id profile)]
|
||||
(st/emit! (rt/nav :dashboard-projects {:team-id team-id}))))
|
||||
|
||||
(mf/defc not-authorized-page
|
||||
(mf/defc not-found
|
||||
[{:keys [error] :as props}]
|
||||
[:section.not-found-layout
|
||||
[:div.not-found-header i/logo]
|
||||
[:div.not-found-content
|
||||
[:div.message-container
|
||||
[:div.error-img i/icon-lock]
|
||||
[:div.main-message "403"]
|
||||
[:div.desc-message "Sorry, you are not authorized to access this page."]
|
||||
#_[:a.btn-primary.btn-small "Go back"]]]])
|
||||
(let [profile (mf/deref refs/profile)]
|
||||
[:section.exception-layout
|
||||
[:div.exception-header
|
||||
{:on-click (partial go-to-dashboard profile)}
|
||||
i/logo]
|
||||
[:div.exception-content
|
||||
[:div.container
|
||||
[:div.image i/icon-empty]
|
||||
[:div.main-message (tr "labels.not-found.main-message")]
|
||||
[:div.desc-message (tr "labels.not-found.desc-message")]
|
||||
[:div.sign-info
|
||||
[:span (tr "labels.not-found.auth-info") " " [:b (:email profile)]]
|
||||
[:a.btn-primary.btn-small
|
||||
{:on-click (st/emitf (da/logout))}
|
||||
(tr "labels.sign-out")]]]]]))
|
||||
|
||||
(mf/defc bad-gateway
|
||||
[{:keys [error] :as props}]
|
||||
(let [profile (mf/deref refs/profile)]
|
||||
[:section.exception-layout
|
||||
[:div.exception-header
|
||||
{:on-click (partial go-to-dashboard profile)}
|
||||
i/logo]
|
||||
[:div.exception-content
|
||||
[:div.container
|
||||
[:div.image i/icon-empty]
|
||||
[:div.main-message (tr "labels.bad-gateway.main-message")]
|
||||
[:div.desc-message (tr "labels.bad-gateway.desc-message")]
|
||||
[:div.sign-info
|
||||
[:a.btn-primary.btn-small
|
||||
{:on-click (st/emitf #(dissoc % :exception))}
|
||||
(tr "labels.retry")]]]]]))
|
||||
|
||||
(mf/defc service-unavailable
|
||||
[{:keys [error] :as props}]
|
||||
(let [profile (mf/deref refs/profile)]
|
||||
[:section.exception-layout
|
||||
[:div.exception-header
|
||||
{:on-click (partial go-to-dashboard profile)}
|
||||
i/logo]
|
||||
[:div.exception-content
|
||||
[:div.container
|
||||
[:div.image i/icon-empty]
|
||||
[:div.main-message (tr "labels.service-unavailable.main-message")]
|
||||
[:div.desc-message (tr "labels.service-unavailable.desc-message")]
|
||||
[:div.sign-info
|
||||
[:a.btn-primary.btn-small
|
||||
{:on-click (st/emitf #(dissoc % :exception))}
|
||||
(tr "labels.retry")]]]]]))
|
||||
|
||||
(mf/defc internal-error
|
||||
[props]
|
||||
(let [profile (mf/deref refs/profile)]
|
||||
[:section.exception-layout
|
||||
[:div.exception-header
|
||||
{:on-click (partial go-to-dashboard profile)}
|
||||
i/logo]
|
||||
[:div.exception-content
|
||||
[:div.container
|
||||
[:div.image i/icon-empty]
|
||||
[:div.main-message "Internal Error"]
|
||||
[:div.desc-message "Something bad happended on backend servers. Please retry the operation and if the problem persists, contact with support."]
|
||||
[:div.sign-info
|
||||
[:a.btn-primary.btn-small
|
||||
{:on-click (st/emitf (dm/assign-exception nil))}
|
||||
(tr "labels.retry")]]]]]))
|
||||
|
||||
(mf/defc exception-page
|
||||
[{:keys [data] :as props}]
|
||||
(case (:type data)
|
||||
:not-found
|
||||
[:& not-found]
|
||||
|
||||
:bad-gateway
|
||||
[:& bad-gateway]
|
||||
|
||||
:service-unavailable
|
||||
[:& service-unavailable]
|
||||
|
||||
:server-error
|
||||
[:& internal-error]
|
||||
|
||||
nil))
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@
|
|||
(ns app.util.router
|
||||
(:refer-clojure :exclude [resolve])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.util.browser-history :as bhistory]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as e]
|
||||
[potok.core :as ptk]
|
||||
[reitit.core :as r]
|
||||
[app.common.data :as d]
|
||||
[app.config :as cfg]
|
||||
[app.util.browser-history :as bhistory]
|
||||
[app.util.timers :as ts])
|
||||
[reitit.core :as r])
|
||||
(:import
|
||||
goog.Uri
|
||||
goog.Uri.QueryData))
|
||||
|
@ -92,6 +92,10 @@
|
|||
;; --- Navigate (Event)
|
||||
|
||||
(deftype Navigate [id params qparams replace]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(dissoc state :exception))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state stream]
|
||||
(let [router (:router state)
|
||||
|
|
Loading…
Add table
Reference in a new issue