diff --git a/.gitignore b/.gitignore index bcd5559..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ /target - -.DS_Store diff --git a/BookForge.toml b/BookForge.toml index 0ac7cc8..3c2268e 100644 --- a/BookForge.toml +++ b/BookForge.toml @@ -1,6 +1,4 @@ database_path = "" -locale = "fr" -base_url = "" [listener] port = 8000 diff --git a/Cargo.lock b/Cargo.lock index ef09c66..232c525 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,15 +99,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "arc-swap" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" -dependencies = [ - "rustversion", -] - [[package]] name = "arrayvec" version = "0.7.6" @@ -296,12 +287,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "base62" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adf9755786e27479693dedd3271691a92b5e242ab139cacb9fb8e7fb5381111" - [[package]] name = "base64" version = "0.22.1" @@ -337,12 +322,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.10.0" @@ -381,11 +360,9 @@ dependencies = [ "askama_web", "axum", "camino", - "csv", "dirs", "log", "pretty_env_logger", - "rust-i18n", "sea-orm", "sea-orm-migration", "serde", @@ -393,7 +370,7 @@ dependencies = [ "snafu", "static-serve", "tokio", - "toml 0.9.11+spec-1.1.0", + "toml", "xdg", ] @@ -420,16 +397,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.19.1" @@ -615,25 +582,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -659,27 +607,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - [[package]] name = "darling" version = "0.20.11" @@ -1069,30 +996,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1399,22 +1302,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -1475,15 +1362,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -1546,7 +1424,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags", "libc", "redox_syscall 0.7.0", ] @@ -1651,15 +1529,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "normpath" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -1915,7 +1784,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit", ] [[package]] @@ -2039,7 +1908,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags", ] [[package]] @@ -2048,7 +1917,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ - "bitflags 2.10.0", + "bitflags", ] [[package]] @@ -2169,60 +2038,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-i18n" -version = "3.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda2551fdfaf6cc5ee283adc15e157047b92ae6535cf80f6d4962d05717dc332" -dependencies = [ - "globwalk", - "once_cell", - "regex", - "rust-i18n-macro", - "rust-i18n-support", - "smallvec", -] - -[[package]] -name = "rust-i18n-macro" -version = "3.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22baf7d7f56656d23ebe24f6bb57a5d40d2bce2a5f1c503e692b5b2fa450f965" -dependencies = [ - "glob", - "once_cell", - "proc-macro2", - "quote", - "rust-i18n-support", - "serde", - "serde_json", - "serde_yaml", - "syn 2.0.114", -] - -[[package]] -name = "rust-i18n-support" -version = "3.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940ed4f52bba4c0152056d771e563b7133ad9607d4384af016a134b58d758f19" -dependencies = [ - "arc-swap", - "base62", - "globwalk", - "itertools 0.11.0", - "lazy_static", - "normpath", - "once_cell", - "proc-macro2", - "regex", - "serde", - "serde_json", - "serde_yaml", - "siphasher", - "toml 0.8.23", - "triomphe", -] - [[package]] name = "rust_decimal" version = "1.40.0" @@ -2266,15 +2081,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schemars" version = "0.9.0" @@ -2330,7 +2136,7 @@ dependencies = [ "chrono", "derive_more", "futures-util", - "itertools 0.14.0", + "itertools", "log", "ouroboros", "pgvector", @@ -2534,15 +2340,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_spanned" version = "1.0.4" @@ -2595,19 +2392,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.13.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "sha1" version = "0.10.6" @@ -2667,12 +2451,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - [[package]] name = "slab" version = "0.4.11" @@ -2835,7 +2613,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64", - "bitflags 2.10.0", + "bitflags", "byteorder", "bytes", "chrono", @@ -2881,7 +2659,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64", - "bitflags 2.10.0", + "bitflags", "byteorder", "chrono", "crc", @@ -3185,18 +2963,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - [[package]] name = "toml" version = "0.9.11+spec-1.1.0" @@ -3205,22 +2971,13 @@ checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap 2.13.0", "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", + "serde_spanned", + "toml_datetime", "toml_parser", "toml_writer", "winnow", ] -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -3230,20 +2987,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.13.0", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - [[package]] name = "toml_edit" version = "0.23.10+spec-1.0.0" @@ -3251,7 +2994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime", "toml_parser", "winnow", ] @@ -3265,12 +3008,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - [[package]] name = "toml_writer" version = "1.0.6+spec-1.1.0" @@ -3352,17 +3089,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "triomphe" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" -dependencies = [ - "arc-swap", - "serde", - "stable_deref_trait", -] - [[package]] name = "typenum" version = "1.19.0" @@ -3408,12 +3134,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "url" version = "2.5.8" @@ -3461,16 +3181,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 83d8600..145fe87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,5 +31,3 @@ pretty_env_logger = "0.5.0" # custom logger log = "0.4.29" serde_with = "3.16.1" -csv = "1.4.0" -rust-i18n = "3.1.5" diff --git a/assets/css/fork-awesome.min.css b/assets/css/fork-awesome.min.css deleted file mode 100755 index fd154dc..0000000 --- a/assets/css/fork-awesome.min.css +++ /dev/null @@ -1,12 +0,0 @@ -/*! -Fork Awesome 1.2.0 -License - https://forkaweso.me/Fork-Awesome/license - -Copyright 2018 Dave Gandy & Fork Awesome - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */@font-face{font-family:ForkAwesome;src:url(../fonts/forkawesome-webfont.eot?v=1.2.0);src:url(../fonts/forkawesome-webfont.eot?#iefix&v=1.2.0) format('embedded-opentype'),url(../fonts/forkawesome-webfont.woff2?v=1.2.0) format('woff2'),url(../fonts/forkawesome-webfont.woff?v=1.2.0) format('woff'),url(../fonts/forkawesome-webfont.ttf?v=1.2.0) format('truetype'),url(../fonts/forkawesome-webfont.svg?v=1.2.0#forkawesomeregular) format('svg');font-weight:400;font-style:normal;font-display:block}.fa{display:inline-block;font:normal normal normal 14px/1 ForkAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell-o:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-globe-e:before{content:"\f304"}.fa-globe-w:before{content:"\f305"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-community:before,.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus-g:before,.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before,.fa-utensils:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before,.fa-pound:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-desc:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-desc:before,.fa-sort-amount-up:before{content:"\f161"}.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-desc:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before,.fa-closed-captioning:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before,.fa-gem:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-medium-square:before{content:"\f2f8"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo-v:before,.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.fa-mastodon:before{content:"\f2e1"}.fa-mastodon-alt:before{content:"\f2e2"}.fa-fork-awesome:before,.fa-fork-circle:before{content:"\f2e3"}.fa-peertube:before{content:"\f2e4"}.fa-diaspora:before{content:"\f2e5"}.fa-friendica:before{content:"\f2e6"}.fa-gnu-social:before{content:"\f2e7"}.fa-liberapay-square:before{content:"\f2e8"}.fa-liberapay:before{content:"\f2e9"}.fa-scuttlebutt:before,.fa-ssb:before{content:"\f2ea"}.fa-hubzilla:before{content:"\f2eb"}.fa-social-home:before{content:"\f2ec"}.fa-artstation:before{content:"\f2ed"}.fa-discord:before{content:"\f2ee"}.fa-discord-alt:before{content:"\f2ef"}.fa-patreon:before{content:"\f2f0"}.fa-snowdrift:before{content:"\f2f1"}.fa-activitypub:before{content:"\f2f2"}.fa-ethereum:before{content:"\f2f3"}.fa-keybase:before{content:"\f2f4"}.fa-shaarli:before{content:"\f2f5"}.fa-shaarli-o:before{content:"\f2f6"}.fa-cut-key:before,.fa-key-modern:before{content:"\f2f7"}.fa-xmpp:before{content:"\f2f9"}.fa-archive-org:before{content:"\f2fc"}.fa-freedombox:before{content:"\f2fd"}.fa-facebook-messenger:before{content:"\f2fe"}.fa-debian:before{content:"\f2ff"}.fa-mastodon-square:before{content:"\f300"}.fa-tipeee:before{content:"\f301"}.fa-react:before{content:"\f302"}.fa-dogmazic:before{content:"\f303"}.fa-zotero:before{content:"\f309"}.fa-nodejs:before{content:"\f308"}.fa-nextcloud:before{content:"\f306"}.fa-nextcloud-square:before{content:"\f307"}.fa-hackaday:before{content:"\f30a"}.fa-laravel:before{content:"\f30b"}.fa-signalapp:before{content:"\f30c"}.fa-gnupg:before{content:"\f30d"}.fa-php:before{content:"\f30e"}.fa-ffmpeg:before{content:"\f30f"}.fa-joplin:before{content:"\f310"}.fa-syncthing:before{content:"\f311"}.fa-inkscape:before{content:"\f312"}.fa-matrix-org:before{content:"\f313"}.fa-pixelfed:before{content:"\f314"}.fa-bootstrap:before{content:"\f315"}.fa-dev-to:before{content:"\f316"}.fa-hashnode:before{content:"\f317"}.fa-jirafeau:before{content:"\f318"}.fa-emby:before{content:"\f319"}.fa-wikidata:before{content:"\f31a"}.fa-gimp:before{content:"\f31b"}.fa-c:before{content:"\f31c"}.fa-digitalocean:before{content:"\f31d"}.fa-att:before{content:"\f31e"}.fa-gitea:before{content:"\f31f"}.fa-file-epub:before{content:"\f321"}.fa-python:before{content:"\f322"}.fa-archlinux:before{content:"\f323"}.fa-pleroma:before{content:"\f324"}.fa-unsplash:before{content:"\f325"}.fa-hackster:before{content:"\f326"}.fa-spell-check:before{content:"\f327"}.fa-moon:before{content:"\f328"}.fa-sun:before{content:"\f329"}.fa-f-droid:before{content:"\f32a"}.fa-biometric:before{content:"\f32b"}.fa-wire:before{content:"\f32c"}.fa-tor-onion:before{content:"\f32e"}.fa-volume-mute:before{content:"\f32f"}.fa-bell-ringing:before{content:"\f32d"}.fa-bell-ringing-o:before{content:"\f330"}.fa-hal:before{content:"\f333"}.fa-jupyter:before{content:"\f335"}.fa-julia:before{content:"\f334"}.fa-classicpress:before{content:"\f331"}.fa-classicpress-circle:before{content:"\f332"}.fa-open-collective:before{content:"\f336"}.fa-orcid:before{content:"\f337"}.fa-researchgate:before{content:"\f338"}.fa-funkwhale:before{content:"\f339"}.fa-askfm:before{content:"\f33a"}.fa-blockstack:before{content:"\f33b"}.fa-boardgamegeek:before{content:"\f33c"}.fa-bunny:before{content:"\f35f"}.fa-buymeacoffee:before{content:"\f33d"}.fa-cc-by:before{content:"\f33e"}.fa-cc-cc:before,.fa-creative-commons-alt:before{content:"\f33f"}.fa-cc-nc-eu:before{content:"\f341"}.fa-cc-nc-jp:before{content:"\f342"}.fa-cc-nc:before{content:"\f340"}.fa-cc-nd:before{content:"\f343"}.fa-cc-pd:before{content:"\f344"}.fa-cc-remix:before{content:"\f345"}.fa-cc-sa:before{content:"\f346"}.fa-cc-share:before{content:"\f347"}.fa-cc-zero:before{content:"\f348"}.fa-conway-glider:before,.fa-conway-hacker:before{content:"\f349"}.fa-csharp:before{content:"\f34a"}.fa-email-bulk:before{content:"\f34b"}.fa-email-bulk-o:before{content:"\f34c"}.fa-gnu:before{content:"\f34d"}.fa-google-play:before{content:"\f34e"}.fa-heroku:before{content:"\f34f"}.fa-hassio:before,.fa-home-assistant:before{content:"\f350"}.fa-java:before{content:"\f351"}.fa-mariadb:before{content:"\f352"}.fa-markdown:before{content:"\f353"}.fa-mysql:before{content:"\f354"}.fa-nordcast:before{content:"\f355"}.fa-plume:before{content:"\f356"}.fa-postgresql:before{content:"\f357"}.fa-sass-alt:before{content:"\f359"}.fa-sass:before{content:"\f358"}.fa-skate:before{content:"\f35a"}.fa-sketchfab:before{content:"\f35b"}.fa-tex:before{content:"\f35c"}.fa-textpattern:before{content:"\f35d"}.fa-unity:before{content:"\f35e"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}/*# sourceMappingURL=fork-awesome.min.css.map */ \ No newline at end of file diff --git a/assets/fonts/forkawesome-webfont.woff2 b/assets/fonts/forkawesome-webfont.woff2 deleted file mode 100755 index c96e5bf..0000000 Binary files a/assets/fonts/forkawesome-webfont.woff2 and /dev/null differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png deleted file mode 100644 index 5dbbdbe..0000000 Binary files a/assets/images/favicon.png and /dev/null differ diff --git a/locales/en.yml b/locales/en.yml deleted file mode 100644 index 5e258e0..0000000 --- a/locales/en.yml +++ /dev/null @@ -1,92 +0,0 @@ -_version: 1 -name: "BookForge" - -common: - download: Download - create: Create - edit: Edit - delete: Delete - next: Next - previous: Previous - actions: Actions - search: Search - reset: Reset - show: Show - no_result: No results - close: Close - confirmation: Confirmation - are_you_sure: Are you sure? - -nav: - toggle: Toggle navigation - books: Books - users: Users - -theme: - light: Light - dark: Dark - -user: - attributes: - name: Name - owner_books: Owned books - borrowed_books: Borrowed books - - index: - title_tag: Users list | BookForge - title: All Users - button: Add a user - - edit: - title_tag: Edit user | BookForge - title: Edit - button: Edit user - - new: - title_tag: New user | BookForge - title: New user - button: Create user - -book: - attributes: - title: Title - authors: Author(s) - description: Description - owner: Owner - current_holder: Current holder - comment: Comment - - index: - title_tag: Books list | BookForge - title: All Books - - new: - title_tag: New book | BookForge - title: New Book - button: Create book - button_short: Add book - - edit: - title_tag: Edit book | BookForge - title: Edit book - button: Edit book - - show: - title_tag: Details | BookForge - book_details: Book details - user_details: User details - more_informations: More information - -footer: - message: Made with love & Fuck fascists! - -error: - error_404: - title_tag: Error 404 | BookForge - title: Oops! This page does not exist - subtitle: 404 NOT FOUND - button: Back to home - - generic: - title_tag: Error | BookForge - title: Oops! An error occurred diff --git a/locales/fr.yml b/locales/fr.yml deleted file mode 100644 index 22b0675..0000000 --- a/locales/fr.yml +++ /dev/null @@ -1,77 +0,0 @@ -_version: 1 -name: "BookForge" -common: - download: Télécharger - create: Créer - edit: Modifier - delete: Supprimer - next: Suivant - previous: Précédent - actions: Actions - search: Filtrer - reset: Réini. - show: Voir - no_result: Aucun résultat - close: Fermer - confirmation: Confirmation - are_you_sure: Êtes-vous sûr ? -nav: - toggle: Basculer la navigation - books: Livres - users: Utilisateurs -theme: - light: Light - dark: Dark -user: - attributes: - name: Nom - owner_books: Livres possédés - borrowed_books: Livres empruntés - index: - title_tag: Liste des utilisateur.ice.s | BookForge - title: Tous les utilisateur.ice.s - button: Ajouter un.e utilisateur.ice - edit: - title_tag: Modifier l'utilisateur.ice | BookForge - title: Modifier - button: Modifier l'utilisateur.ice - new: - title_tag: Nouvel utilisateur.ice | BookForge - title: Nouvel utilisateur.ice - button: Créer l'utilisateur.ice -book: - attributes: - title: Titre - authors: Auteur.ice.(s) - description: Description - owner: Propriétaire - current_holder: Détenteur.ice actuel.le - comment: Commentaire - index: - title_tag: Liste des livres | BookForge - title: Tous les livres - new: - title_tag: Nouveau livre | BookForge - title: Nouveau livre - button: Créer le livre - button_short: Ajouter un livre - edit: - title_tag: Modifier le livre | BookForge - title: Modifier le livre - button: Modifier le livre - show: - title_tag: Details | BookForge - book_details: Détails du livre - user_details: Détails de l'utilisateur.ice - more_informations: Plus d'informations -footer: - message: Fait avec amour & Nique les fachos ! -error: - error_404: - title_tag: Erreur 404 | BookForge - title: Oups ! Cette page n'existe pas - subtitle: 404 NOT FOUND - button: Retour à l'accueil - generic: - title_tag: Erreur | BookForge - title: Oups ! Une erreur s'est produite diff --git a/src/lib.rs b/src/lib.rs index 6c3de23..73e6cac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,10 @@ use askama::Template; use askama_web::WebTemplate; use axum::{ Router, - extract::State, routing::{get, post}, }; use static_serve::embed_assets; -use crate::routes::router::Router as InternalRouter; use crate::state::AppState; mod migrations; @@ -15,24 +13,17 @@ mod models; mod routes; pub mod state; -#[macro_use] -extern crate rust_i18n; - -i18n!("locales", fallback = "en", minify_key = true); - pub fn build_app(state: AppState) -> Router { - rust_i18n::set_locale(&state.config.locale); embed_assets!("assets", compress = true); Router::new() .route("/", get(routes::book::index)) - .route("/books/new", get(routes::book::new)) .route("/books", post(routes::book::create)) .route("/books/{id}", get(routes::book::show)) .route("/books/{id}", post(routes::book::update)) .route("/books/{id}/delete", post(routes::book::delete)) .route("/books/{id}/edit", get(routes::book::edit)) - .route("/books/download_csv", get(routes::book::download_csv)) + .route("/books/new", get(routes::book::new)) .route("/users", get(routes::user::index)) .route("/users/new", get(routes::user::new)) .route("/users/{id}/edit", get(routes::user::edit)) @@ -46,14 +37,8 @@ pub fn build_app(state: AppState) -> Router { #[derive(Template, WebTemplate)] #[template(path = "404.html")] -struct NotFoundTemplate { - pub router: InternalRouter, -} +struct NotFoundTemplate {} -pub async fn error_handler(State(state): State) -> impl axum::response::IntoResponse { - NotFoundTemplate { - router: InternalRouter { - base_path: state.config.base_path, - }, - } +pub async fn error_handler() -> impl axum::response::IntoResponse { + NotFoundTemplate {} } diff --git a/src/models/book.rs b/src/models/book.rs index b61753a..e8a5898 100644 --- a/src/models/book.rs +++ b/src/models/book.rs @@ -70,7 +70,7 @@ impl BookOperator { /// Lists all books matching the optional query filters. /// /// Results are ordered by ID in descending order (newest first). - pub async fn all(&self) -> Result, BookError> { + pub async fn list(&self) -> Result, BookError> { Entity::find() .order_by_desc(Column::Id) .all(&self.state.db) @@ -78,18 +78,7 @@ impl BookOperator { .context(DBSnafu) } - pub async fn all_filtered(&self, query: Option) -> Result, BookError> { - let conditions = Self::filter_conditions(query); - - Entity::find() - .filter(conditions) - .order_by_desc(Column::Id) - .all(&self.state.db) - .await - .context(DBSnafu) - } - - pub async fn all_paginate( + pub async fn list_paginate( &self, page: u64, query: Option, @@ -97,7 +86,24 @@ impl BookOperator { let page = if page > 0 { page } else { 1 }; // keep 1-indexed let page_0indexed = page - 1; // convert for SeaORM (0-based index) - let conditions = Self::filter_conditions(query); + let mut conditions = Condition::all(); + if let Some(book_query) = query { + if let Some(title) = book_query.title { + conditions = conditions.add(Column::Title.contains(&title)); + } + + if let Some(authors) = book_query.authors { + conditions = conditions.add(Column::Authors.contains(&authors)); + } + + if let Some(owner_id) = book_query.owner_id { + conditions = conditions.add(Column::OwnerId.eq(owner_id)); + } + + if let Some(current_holder_id) = book_query.current_holder_id { + conditions = conditions.add(Column::CurrentHolderId.eq(current_holder_id)); + } + } let book_pages = Entity::find() .filter(conditions) @@ -203,28 +209,4 @@ impl BookOperator { book.delete(&self.state.db).await.context(DBSnafu) } - - // private - - fn filter_conditions(query: Option) -> Condition { - let mut conditions = Condition::all(); - if let Some(book_query) = query { - if let Some(title) = book_query.title { - conditions = conditions.add(Column::Title.contains(&title)); - } - - if let Some(authors) = book_query.authors { - conditions = conditions.add(Column::Authors.contains(&authors)); - } - - if let Some(owner_id) = book_query.owner_id { - conditions = conditions.add(Column::OwnerId.eq(owner_id)); - } - - if let Some(current_holder_id) = book_query.current_holder_id { - conditions = conditions.add(Column::CurrentHolderId.eq(current_holder_id)); - } - } - return conditions; - } } diff --git a/src/models/user.rs b/src/models/user.rs index 0ce96eb..bdf2624 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,11 +1,9 @@ use crate::models::book; use crate::routes::book::BookForm; -use crate::routes::user::IndexQuery; use crate::routes::user::UserForm; use crate::state::AppState; use crate::state::error::UserSnafu; use sea_orm::ActiveValue::Set; -use sea_orm::Condition; use sea_orm::DeleteResult; use sea_orm::entity::prelude::*; use snafu::ResultExt; @@ -55,23 +53,10 @@ impl UserOperator { Self { state } } - pub async fn all(&self) -> Result, UserError> { + pub async fn list(&self) -> Result, UserError> { Entity::find().all(&self.state.db).await.context(DBSnafu) } - pub async fn all_filtered(&self, query: IndexQuery) -> Result, UserError> { - let mut conditions = Condition::all(); - if let Some(name) = query.name { - conditions = conditions.add(Column::Name.contains(name)) - } - - Entity::find() - .filter(conditions) - .all(&self.state.db) - .await - .context(DBSnafu) - } - pub async fn find_by_id(&self, id: i32) -> Result { let user: Option = Entity::find_by_id(id) .one(&self.state.db) diff --git a/src/routes/book.rs b/src/routes/book.rs index 8d4fa03..26545fb 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -4,17 +4,15 @@ use askama::Template; use askama_web::WebTemplate; use axum::{ Form, - body::Body, extract::{Path, Query, State}, - response::{IntoResponse, Redirect, Response}, + response::{IntoResponse, Redirect}, }; -use csv::Writer; use serde::Deserialize; use serde_with::{NoneAsEmptyString, serde_as}; use snafu::prelude::*; -use crate::{models::book::Model as BookModel, routes::router::Router, state::error::CSVSnafu}; -use crate::{models::user::Model as UserModel, state::error::IOSnafu}; +use crate::models::book::Model as BookModel; +use crate::models::user::Model as UserModel; use crate::{ models::{book::BookOperator, user::UserOperator}, @@ -55,7 +53,6 @@ struct BookIndexTemplate { current_page: u64, total_page: u64, base_query: String, - router: Router, } pub async fn index( @@ -69,13 +66,13 @@ pub async fn index( // Get all Users let users = UserOperator::new(state.clone()) - .all() + .list() .await .context(UserSnafu)?; // Get all Book filtered with query - let books_paginate = BookOperator::new(state.clone()) - .all_paginate(page, Some(query.clone())) + let books_paginate = BookOperator::new(state) + .list_paginate(page, Some(query.clone())) .await .context(BookSnafu)?; @@ -128,9 +125,6 @@ pub async fn index( current_page: books_paginate.current_page, total_page: books_paginate.total_page, base_query, - router: Router { - base_path: state.config.base_path, - }, }) } @@ -140,7 +134,6 @@ struct ShowBookTemplate { book: BookModel, owner: UserModel, current_holder: Option, - router: Router, } pub async fn show( @@ -173,9 +166,6 @@ pub async fn show( book, owner, current_holder, - router: Router { - base_path: state.config.base_path, - }, }) } @@ -208,23 +198,14 @@ pub async fn create( #[template(path = "books/new.html")] struct NewBookTemplate { users: Vec, - router: Router, } pub async fn new( State(state): State, ) -> Result { - let users = UserOperator::new(state.clone()) - .all() - .await - .context(UserSnafu)?; + let users = UserOperator::new(state).list().await.context(UserSnafu)?; - Ok(NewBookTemplate { - users, - router: Router { - base_path: state.config.base_path, - }, - }) + Ok(NewBookTemplate { users }) } #[derive(Template, WebTemplate)] @@ -232,7 +213,6 @@ pub async fn new( struct EditBookTemplate { users: Vec, book: BookModel, - router: Router, } pub async fn edit( @@ -240,21 +220,15 @@ pub async fn edit( Path(id): Path, ) -> Result { let users = UserOperator::new(state.clone()) - .all() + .list() .await .context(UserSnafu)?; - let book = BookOperator::new(state.clone()) + let book = BookOperator::new(state) .find_by_id(id) .await .context(BookSnafu)?; - Ok(EditBookTemplate { - users, - book, - router: Router { - base_path: state.config.base_path, - }, - }) + Ok(EditBookTemplate { users, book }) } pub async fn update( @@ -280,75 +254,3 @@ pub async fn delete( Ok(Redirect::to("/").into_response()) } - -/// Download CSV filter (no paginate) of all books -pub async fn download_csv( - State(state): State, - Query(query): Query, -) -> Result { - let books = BookOperator::new(state.clone()) - .all_filtered(Some(query)) - .await - .context(BookSnafu)?; - - let users = UserOperator::new(state).all().await.context(UserSnafu)?; - - let users_by_id: HashMap = users.into_iter().map(|u| (u.id, u)).collect(); - - let mut wtr = Writer::from_writer(vec![]); - wtr.write_record(&[ - "ID", - "Title", - "Author(s)", - "Description", - "Owner", - "Current Holder", - "Comment", - ]) - .context(CSVSnafu)?; - - for book in books { - let owner_format = match users_by_id.get(&book.owner_id).cloned().ok_or(UserSnafu) { - Ok(owner) => format!("{} (id: {})", owner.name.to_string(), owner.id), - Err(_) => "-".to_string(), - }; - - let current_holder = match users_by_id - // if current_holder_id is None, take 0. - // So get returns errors because user with id 0 can't exist - .get(&book.current_holder_id.unwrap_or(0)) - .cloned() - .ok_or(UserSnafu) - { - Ok(current_holder) => format!( - "{} (id: {})", - current_holder.name.to_string(), - current_holder.id - ), - Err(_) => "-".to_string(), - }; - - wtr.write_record(&[ - book.id.to_string(), - book.title, - book.authors, - book.description.unwrap_or_default(), - owner_format, - current_holder, - book.comment.unwrap_or_default(), - ]) - .context(CSVSnafu)?; - } - - wtr.flush().context(IOSnafu)?; - - let csv_bytes = wtr.into_inner(); - - match csv_bytes { - Ok(csv_bytes) => Ok(Response::builder() - .header("Content-Type", "text/csv") - .body(Body::from(csv_bytes)) - .unwrap()), - Err(_) => Ok(Redirect::to("/").into_response()), - } -} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 2bbba56..011ff79 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,3 +1,2 @@ pub mod book; -pub mod router; pub mod user; diff --git a/src/routes/router.rs b/src/routes/router.rs deleted file mode 100644 index 52bcd0b..0000000 --- a/src/routes/router.rs +++ /dev/null @@ -1,54 +0,0 @@ -#[derive(Clone)] -pub struct Router { - pub base_path: String, -} - -impl Router { - pub fn assets(&self, path: &str) -> String { - if self.base_path.is_empty() || self.base_path == "/" { - format!("/{}", path) - } else { - format!("{}/{}", self.base_path.trim_end_matches('/'), path) - } - } - - pub fn root_path(&self) -> String { - format!("{}/", &self.base_path) - } - - // BOOKS ROUTES - - pub fn new_book_path(&self) -> String { - format!("{}/books/new", &self.base_path) - } - - pub fn create_book_path(&self) -> String { - format!("{}/books", &self.base_path) - } - - pub fn update_book_path(&self, id: &i32) -> String { - format!("{}/books/{}", &self.base_path, id) - } - - pub fn download_csv_book_path(&self) -> String { - format!("{}/books/download_csv", &self.base_path) - } - - // USERS - - pub fn index_user_path(&self) -> String { - format!("{}/users", &self.base_path) - } - - pub fn new_user_path(&self) -> String { - format!("{}/users/new", &self.base_path) - } - - pub fn create_user_path(&self) -> String { - format!("{}/users", &self.base_path) - } - - pub fn update_user_path(&self, id: &i32) -> String { - format!("{}/users/{}", &self.base_path, id) - } -} diff --git a/src/routes/user.rs b/src/routes/user.rs index 04eed56..b245d42 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -4,11 +4,10 @@ use askama::Template; use askama_web::WebTemplate; use axum::{ Form, - extract::{Path, Query, State}, + extract::{Path, State}, response::Redirect, }; use serde::Deserialize; -use serde_with::{NoneAsEmptyString, serde_as}; use snafu::prelude::*; use crate::{ @@ -16,7 +15,6 @@ use crate::{ book::BookOperator, user::{self, UserOperator}, }, - routes::router::Router, state::{ AppState, error::{AppStateError, BookSnafu, UserSnafu}, @@ -26,9 +24,7 @@ use crate::{ #[derive(Template, WebTemplate)] #[template(path = "users/index.html")] struct UsersIndexTemplate { - users_with_books_number: Vec, - query: IndexQuery, - router: Router, + user_with_books_number: Vec, } pub struct UserWithBookNumber { @@ -40,25 +36,16 @@ pub struct UserWithBookNumber { pub borrowed_book_number: usize, } -#[serde_as] -#[derive(Deserialize, Clone)] -pub struct IndexQuery { - #[serde(default)] - #[serde_as(as = "NoneAsEmptyString")] - pub name: Option, -} - pub async fn index( State(state): State, - Query(query): Query, ) -> Result { let users = UserOperator::new(state.clone()) - .all_filtered(query.clone()) + .list() .await .context(UserSnafu)?; let books = BookOperator::new(state.clone()) - .all() + .list() .await .context(BookSnafu)?; @@ -86,11 +73,7 @@ pub async fn index( } Ok(UsersIndexTemplate { - users_with_books_number: result, - query, - router: Router { - base_path: state.config.base_path, - }, + user_with_books_number: result, }) } @@ -140,36 +123,24 @@ pub async fn delete( #[template(path = "users/edit.html")] struct EditTemplate { user: user::Model, - router: Router, } pub async fn edit( State(state): State, Path(id): Path, ) -> Result { - let user = UserOperator::new(state.clone()) + let user = UserOperator::new(state) .find_by_id(id) .await .context(UserSnafu)?; - Ok(EditTemplate { - user, - router: Router { - base_path: state.config.base_path, - }, - }) + Ok(EditTemplate { user }) } #[derive(Template, WebTemplate)] #[template(path = "users/new.html")] -struct NewTemplate { - router: Router, -} +struct NewTemplate {} -pub async fn new(State(state): State) -> impl axum::response::IntoResponse { - NewTemplate { - router: Router { - base_path: state.config.base_path, - }, - } +pub async fn new() -> impl axum::response::IntoResponse { + NewTemplate {} } diff --git a/src/state/config.rs b/src/state/config.rs index f137f8a..4f81560 100644 --- a/src/state/config.rs +++ b/src/state/config.rs @@ -32,8 +32,6 @@ pub enum ConfigError { pub struct AppConfig { #[serde(default = "AppConfig::default_sqlite_path")] pub database_path: Utf8PathBuf, - pub locale: String, - pub base_path: String, pub listener: Listener, } @@ -41,8 +39,6 @@ impl Default for AppConfig { fn default() -> Self { AppConfig { database_path: Self::default_sqlite_path(), - base_path: Self::default_base_path(), - locale: Self::default_locale(), listener: Listener::default(), } } @@ -96,14 +92,6 @@ impl AppConfig { Self::config_path().join("BookForge.toml") } - fn default_locale() -> String { - "en".to_string() - } - - fn default_base_path() -> String { - "".to_string() - } - pub fn default_sqlite_path() -> Utf8PathBuf { Self::config_path().join("db.sqlite") } diff --git a/src/state/error.rs b/src/state/error.rs index 6a031b6..e3b6a39 100644 --- a/src/state/error.rs +++ b/src/state/error.rs @@ -6,7 +6,6 @@ use snafu::prelude::*; use crate::{ models::{book::BookError, user::UserError}, - routes::router::Router, state::config::ConfigError, }; @@ -34,21 +33,12 @@ pub enum AppStateError { Book { source: BookError, }, - #[snafu(display("CSV Error"))] - CSV { - source: csv::Error, - }, - #[snafu(display("IO Error"))] - IO { - source: std::io::Error, - }, } #[derive(Template, WebTemplate)] #[template(path = "error.html")] struct ErrorTemplate { state: AppStateErrorContext, - router: Router, } struct AppStateErrorContext { @@ -68,9 +58,6 @@ impl IntoResponse for AppStateError { let error_context = AppStateErrorContext::from(self); ErrorTemplate { state: error_context, - router: Router { - base_path: "".to_string(), - }, } .into_response() } diff --git a/templates/404.html b/templates/404.html index 6f35ef6..854fcc8 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,13 +1,8 @@ {% extends "base.html" %} - -{% block title %} - {{ t!("error.error_404.title_tag") }} -{% endblock %} - {% block main %}
-

{{ t!("error.error_404.title") }}

-

{{ t!("error.error_404.subtitle") }}

- {{ t!("error.error_404.button") }} +

Oops ! This page does not exist

+

404 NOT FOUND

+ Back to home
{% endblock %} diff --git a/templates/base.html b/templates/base.html index bc0ed7e..58b2ef0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -2,14 +2,13 @@ - {% block title %}{{ t!("name") }}{% endblock %} + {% block title %}Book Forge{% endblock %} - - - + + - - + + {% block extra_head %}{% endblock extra_head %} @@ -26,7 +25,7 @@ {% endblock %} - - + + diff --git a/templates/books/edit.html b/templates/books/edit.html index f80be6a..6964b99 100644 --- a/templates/books/edit.html +++ b/templates/books/edit.html @@ -2,27 +2,23 @@ {% import "components/typography.html" as typography %} {% import "components/cards.html" as cards %} -{% block title %} - {{ t!("book.edit.title_tag") }} -{% endblock %} - {% block main %} - {{ typography::heading(t!("book.edit.title")) }} + {{ typography::heading("Editer") }} {% call cards::card() %} -
+
- +
- +
- + {% match book.current_holder_id %} @@ -56,7 +52,7 @@
- + {% match book.description %} {% when Some with (description) %} @@ -66,7 +62,7 @@
- + {% match book.comment %} {% when Some with (comment) %} @@ -76,7 +72,7 @@
- +
{% endcall %} diff --git a/templates/books/new.html b/templates/books/new.html index 30deb91..ddd66ce 100644 --- a/templates/books/new.html +++ b/templates/books/new.html @@ -3,32 +3,28 @@ {% import "components/cards.html" as cards %} {% import "components/inputs.html" as form_helpers %} -{% block title %} - {{ t!("book.new.title_tag") }} -{% endblock %} - {% block main %} - {{ typography::heading(t!("book.new.title")) }} + {{ typography::heading("New book") }} {% call cards::card() %} -
- {{ form_helpers::input("title", t!("book.attributes.title"), is_required = true, placeholder = "Ex: La Petite Dernière") }} - {{ form_helpers::input("authors", t!("book.attributes.authors"), is_required = true, placeholder = "Ex: Fatima Daas") }} + + {{ form_helpers::input("title", "Name", is_required = true, placeholder = "Ex: La Petite Dernière") }} + {{ form_helpers::input("authors", "Author(s)", is_required = true, placeholder = "Ex: Fatima Daas") }} - {% call(option) form_helpers::select("owner_id", t!("book.attributes.owner"), users, is_required = true) %} + {% call(option) form_helpers::select("owner_id", "Owner", users, is_required = true) %} {% endcall %} - {% call(option) form_helpers::select("current_holder_id", t!("book.attributes.current_holder"), users, is_required = false) %} + {% call(option) form_helpers::select("current_holder_id", "Current Holder", users, is_required = false) %} {% endcall %} + + {{ form_helpers::textarea("description", "Description", rows = 5, is_required = false, placeholder = "Ex: Je m’appelle Fatima Daas. Je suis la mazoziya, la petite dernière. Celle à laquelle on ne s’est pas préparé. Française d’origine algérienne.") }} - {{ form_helpers::textarea("description", t!("book.attributes.description"), rows = 5, is_required = false, placeholder = "Ex: Je m’appelle Fatima Daas. Je suis la mazoziya, la petite dernière. Celle à laquelle on ne s’est pas préparé. Française d’origine algérienne.") }} - - {{ form_helpers::textarea("comment", t!("book.attributes.comment"), rows = 3, is_required = false, placeholder = "Ex: I recommend it, it's great!") }} + {{ form_helpers::textarea("comment", "Comment", rows = 3, is_required = false, placeholder = "Ex: I recommend it, it's great!") }}
- +
{% endcall %} diff --git a/templates/books/show.html b/templates/books/show.html index 61042f2..aec5bda 100644 --- a/templates/books/show.html +++ b/templates/books/show.html @@ -4,43 +4,39 @@ {% import "components/fields.html" as fields %} {% import "components/cards.html" as cards %} -{% block title %} - {{ t!("book.show.title_tag") }} -{% endblock %} - {% block main %} {{ typography::book_heading(book.title, book, show = false) }} {% call cards::card() %} -
-
{{ t!("book.show.book_details") }}
- {{ fields::field(t!("book.attributes.title"), book.title) }} - {{ fields::field(t!("book.attributes.authors"), book.authors) }} +
+
Book details
+ {{ fields::field("Name", book.title) }} + {{ fields::field("Authors", book.authors) }} {% match book.description %} {% when Some with (description) %} - {{ fields::field(t!("book.attributes.description"), description) }} + {{ fields::field("Description", description) }} {% when None %} - {{ fields::field(t!("book.attributes.description"), "-") }} + {{ fields::field("Description", "-") }} {% endmatch %} -
{{ t!("book.show.user_details") }}
- {{ fields::field(t!("book.attributes.owner"), owner.name) }} +
User Details
+ {{ fields::field("Owner", owner.name) }} {% match current_holder %} {% when Some with (current_holder) %} - {{ fields::field(t!("book.attributes.current_holder"), current_holder.name) }} + {{ fields::field("Current Holder", current_holder.name) }} {% when None %} - {{ fields::field(t!("book.attributes.current_holder"), "-") }} + {{ fields::field("Current Holder", "-") }} {% endmatch %} -
{{ t!("book.show.more_informations") }}
+
More Informations
{% match book.comment %} {% when Some with (comment) %} - {{ fields::field(t!("book.attributes.comment"), comment) }} + {{ fields::field("Comment", comment) }} {% when None %} - {{ fields::field(t!("book.attributes.comment"), "-") }} + {{ fields::field("Comment", "-") }} {% endmatch %}
{% endcall %} diff --git a/templates/components/dropdown.html b/templates/components/dropdown.html index 7b0e55d..37dc290 100644 --- a/templates/components/dropdown.html +++ b/templates/components/dropdown.html @@ -14,15 +14,15 @@ {% macro crud_dropdown_button(book, label, sub_path, show = true) %} @@ -32,16 +32,16 @@ {% endmacro %} diff --git a/templates/error.html b/templates/error.html index 09e125b..e84c368 100644 --- a/templates/error.html +++ b/templates/error.html @@ -1,17 +1,12 @@ {% extends "base.html" %} - -{% block title %} - {{ t!("error.generic.title_tag") }} -{% endblock %} - {% block main %}
-

{{ t!("error.generic.title") }}

+

Oops ! An Error occured

{% for error in state.errors %}

{{ error }}

{% endfor %}
- {{ t!("error.error_404.button") }} + Back to home
{% endblock %} diff --git a/templates/index.html b/templates/index.html index 2701d7b..183f730 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3,22 +3,14 @@ {% import "components/dropdown.html" as dropdown %} {% import "components/cards.html" as cards %} -{% block title %} - {{ t!("book.index.title_tag") }} -{% endblock %} - {% block main %} - {% call typography::heading(t!("book.index.title")) %} - - {{ t!("common.download") }} (csv) - - {% endcall %} + {{ typography::heading("All books") }} {% call cards::card() %}
- + {% match query.title %} {% when Some with (value) %} @@ -28,7 +20,7 @@
- + {% match query.authors %} {% when Some with (value) %} @@ -39,7 +31,7 @@
- + @@ -83,84 +75,73 @@
- +
{% endcall %} {% call cards::card() %} - {% if books_with_user.is_empty() %} -
-

{{ t!("common.no_result") }}

- - - {{ t!("book.new.button_short") }} - -
- {% else %} - - - - - - - - - - - - - {% for book_user in books_with_user %} - - - - - - - - - {% endfor %} - -
#{{ t!("book.attributes.title") }}{{ t!("book.attributes.authors") }}{{ t!("book.attributes.owner") }}{{ t!("book.attributes.current_holder") }}{{ t!("common.actions") }}
{{ book_user.book.id }}{{ book_user.book.title }}{{ book_user.book.authors }}{{ book_user.owner.name }} - {% match book_user.current_holder %} - {% when Some with (current_holder) %} - {{ current_holder.name }} - {% when None %} - - - {% endmatch %} - - {{ dropdown::crud_dropdown_button(book_user.book, t!("common.actions"), "books") }} -
- - {% if total_page > 1 %} -
- -
- {% endif %} + + + + + + + + + + + + + {% for book_user in books_with_user %} + + + + + + + + + {% endfor %} + +
#NameAuthor(s)OwnerCurrent HolderActions
{{ book_user.book.id }}{{ book_user.book.title }}{{ book_user.book.authors }}{{ book_user.owner.name }} + {% match book_user.current_holder %} + {% when Some with (current_holder) %} + {{ current_holder.name }} + {% when None %} + - + {% endmatch %} + + {{ dropdown::crud_dropdown_button(book_user.book, "Actions", "books") }} +
+ {% if total_page > 1 %} +
+ +
{% endif %} {% endcall %} {% endblock %} diff --git a/templates/layout/footer.html b/templates/layout/footer.html index 3f29c8b..3ddc19e 100644 --- a/templates/layout/footer.html +++ b/templates/layout/footer.html @@ -1,5 +1,5 @@
-

{{ t!("footer.message") }}

+

Made with Love & Fuck a Fascist !

-
+
\ No newline at end of file diff --git a/templates/layout/header.html b/templates/layout/header.html index 114aa1f..3f64943 100644 --- a/templates/layout/header.html +++ b/templates/layout/header.html @@ -1,33 +1,34 @@ + \ No newline at end of file diff --git a/templates/users/edit.html b/templates/users/edit.html index 017ef04..98d6669 100644 --- a/templates/users/edit.html +++ b/templates/users/edit.html @@ -1,24 +1,20 @@ {% extends "base.html" %} {% import "components/typography.html" as typography %} {% import "components/cards.html" as cards %} -{% import "components/inputs.html" as form_helpers %} - -{% block title %} - {{ t!("book.edit.title_tag") }} -{% endblock %} +o{% import "components/inputs.html" as form_helpers %} {% block main %} - {{ typography::heading(t!("user.edit.title")) }} + {{ typography::heading("New User") }} {% call cards::card() %} -
+
- {{ form_helpers::input("name", t!("user.attributes.name"), value = user.name, is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }} + {{ form_helpers::input("name", "Name", value = user.name, is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }}
- +
diff --git a/templates/users/index.html b/templates/users/index.html index 835aa99..04aedae 100644 --- a/templates/users/index.html +++ b/templates/users/index.html @@ -4,73 +4,35 @@ {% import "components/cards.html" as cards %} {% import "components/inputs.html" as form_helpers %} -{% block title %} - {{ t!("user.index.title_tag") }} -{% endblock %} - {% block main %} - {% call typography::heading(t!("user.index.title")) %} - {{ t!("user.index.button") }} + {% call typography::heading("All users") %} + Add User {% endcall %} {% call cards::card() %} -
-
-
- - {% match query.name %} - {% when Some with (value) %} - - {% when None %} - - {% endmatch %} -
- -
- -
- - -
-
- {% endcall %} - - {% call cards::card() %} - {% if users_with_books_number.is_empty() %} -
-

{{ t!("common.no_result") }}

- - - {{ t!("user.index.button") }} - -
- {% else %} - - - - - - - - +
#{{ t!("user.attributes.name") }}{{ t!("user.attributes.owner_books") }}{{ t!("user.attributes.borrowed_books") }}{{ t!("common.actions") }}
+ + + + + + + + + + + {% for user_information in user_with_books_number %} + + + + + + - - - {% for user_information in users_with_books_number %} - - - - - - - - {% endfor %} - -
#NameOwner bookBorrowed bookActions
{{ user_information.user.id }}{{ user_information.user.name }}{{ user_information.owner_book_number }}{{ user_information.borrowed_book_number }} + {{ dropdown::crud_dropdown_button(user_information.user, "Actions", "users", show = false) }} +
{{ user_information.user.id }}{{ user_information.user.name }}{{ user_information.owner_book_number }}{{ user_information.borrowed_book_number }} - {{ dropdown::crud_dropdown_button(user_information.user, t!("common.actions"), "users", show = false) }} -
- {% endif %} + {% endfor %} + + {% endcall %} {% endblock %} diff --git a/templates/users/new.html b/templates/users/new.html index 6213b3a..c3ebcd4 100644 --- a/templates/users/new.html +++ b/templates/users/new.html @@ -1,23 +1,19 @@ {% extends "base.html" %} {% import "components/typography.html" as typography %} {% import "components/cards.html" as cards %} -{% import "components/inputs.html" as form_helpers %} - -{% block title %} - {{ t!("user.new.title_tag") }} -{% endblock %} +o{% import "components/inputs.html" as form_helpers %} {% block main %} - {{ typography::heading(t!("user.new.title")) }} + {{ typography::heading("New User") }} {% call cards::card() %} -
+
- {{ form_helpers::input("name", t!("user.attributes.name"), is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }} + {{ form_helpers::input("name", "Name", is_required = true, placeholder = "Ex: Kropotkine", margin_bottom = false) }}
- +