fix admin

This commit is contained in:
tdurieux committed 2026-05-03 22:29:01 +02:00
1 parent 6096cb0744
commit db2ac5307d
6 files changed
+383 -68

No files matched your search

+1 -1
View File
@@ -6,4 +6,4 @@
*/:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unsLine truncated */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unsLine truncated
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com * Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.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(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{contentLine truncated */.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:solid .08em #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.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(360deg);transform:rotate(360deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{contentLine truncated
+199 -29
View File
@@ -1037,21 +1037,53 @@ a:hover {
} }
.status-bar { .status-bar {
background-color: var(--main-bg-color); display: flex;
padding: 8px 6px; align-items: center;
gap: 12px;
background-color: var(--canvas-bg-color);
padding: 10px 14px;
margin: 0; margin: 0;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
border-radius: 0; border-radius: 0;
flex-shrink: 0;
}
.status-bar-actions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.status-bar-actions .btn {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--paper-card);
border: 1px solid var(--border-color);
color: var(--color);
border-radius: 6px;
padding: 4px 10px;
font-size: 12.5px;
min-height: 30px;
}
.status-bar-actions .btn:hover {
background: var(--hover-bg-color);
color: var(--color);
} }
.paths { .paths {
flex: 1 1 auto;
min-width: 0;
padding: 0; padding: 0;
margin: 0; margin: 0;
background-color: initial; background-color: initial;
border: none; border: none;
border-radius: 0; border-radius: 0;
align-items: center; align-items: center;
overflow-x: auto;
white-space: nowrap;
flex-wrap: nowrap;
} }
.paths::-webkit-scrollbar { display: none; }
.paths a { .paths a {
color: var(--color); color: var(--color);
@@ -1988,22 +2020,27 @@ code {
font-size: 16px; /* Prevents iOS zoom */ font-size: 16px; /* Prevents iOS zoom */
} }
/* File explorer mobile layout */
.leftCol {
max-height: 40vh;
border-bottom: 2px solid var(--border-color);
}
/* Status bar wrapping */ /* Status bar wrapping */
.status-bar { .status-bar {
flex-wrap: wrap; gap: 6px;
gap: 4px; padding: 8px 10px;
} }
.status-bar .btn { .status-bar .btn {
font-size: 12px; font-size: 12px;
padding: 3px 8px; padding: 4px 10px;
min-height: 28px; min-height: 32px;
}
.status-bar .paths {
font-size: 11.5px;
overflow-x: auto;
flex-wrap: nowrap;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.status-bar .paths::-webkit-scrollbar { display: none; }
.status-bar-actions {
flex-shrink: 0;
gap: 4px;
} }
/* Home hero section */ /* Home hero section */
@@ -2155,27 +2192,160 @@ code {
} }
} }
/* Mobile toggle for file explorer sidebar */ /* ===== Explorer page (paper) ===== */
.sidebar-toggle { .explorer-page {
display: none; display: flex;
height: 100%;
width: 100%; width: 100%;
padding: 8px; background: var(--canvas-bg-color);
text-align: center; position: relative;
background: var(--sidebar-bg-color); }
border: 1px solid var(--border-color); .explorer-main {
color: var(--color); flex: 1 1 auto;
cursor: pointer; min-width: 0;
font-size: 14px; display: flex;
flex-direction: column;
overflow: hidden;
background: var(--canvas-bg-color);
}
.explorer-content {
flex: 1 1 auto;
min-height: 0;
width: 100%;
overflow: auto;
} }
@media (max-width: 767px) { .leftCol-head,
.sidebar-toggle { .leftCol-foot,
display: block; .leftCol-close {
} display: none;
}
.leftCol-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
}
.leftCol-foot {
padding: 8px 12px;
border-top: 1px solid var(--border-color);
background: var(--paper-bg-alt);
}
.leftCol-foot .last-update {
border-top: none;
padding: 0;
}
.leftCol-backdrop { display: none; }
.leftCol.collapsed { .paper-inline-warning {
display: none !important; display: flex;
align-items: flex-start;
gap: 8px;
margin: 8px;
padding: 10px 12px;
background: rgba(138, 107, 30, 0.08);
border: 1px solid rgba(138, 107, 30, 0.35);
border-radius: 8px;
color: var(--color);
font-size: 12.5px;
line-height: 1.4;
}
.paper-inline-warning i { color: #8A6B1E; margin-top: 2px; flex-shrink: 0; }
.dark-mode .paper-inline-warning i { color: #FFD37A; }
@media (min-width: 768px) {
.leftCol-body { padding: 8px 4px; }
}
/* Mobile / tablet toggle */
.sidebar-toggle {
display: none;
align-items: center;
gap: 8px;
position: fixed;
bottom: 18px;
left: 18px;
z-index: 1100;
padding: 10px 14px;
background: var(--color);
color: var(--canvas-bg-color);
border: 1px solid var(--color);
border-radius: 999px;
font-family: var(--font-sans);
font-size: 13px;
font-weight: 500;
box-shadow: 0 6px 20px rgba(0,0,0,0.15);
cursor: pointer;
outline: none;
}
.sidebar-toggle:hover { background: var(--primary-hover-bg); }
.sidebar-toggle:focus,
.sidebar-toggle:focus-visible { outline: none; box-shadow: 0 6px 20px rgba(0,0,0,0.15); }
@media (max-width: 991px) {
.sidebar-toggle { display: inline-flex; }
/* Sidebar becomes a slide-in drawer */
.leftCol {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: min(86vw, 340px);
max-width: 340px;
z-index: 1200;
border: none;
border-right: 1px solid var(--border-color);
transform: translateX(0);
transition: transform 0.22s ease;
box-shadow: 4px 0 24px rgba(0,0,0,0.18);
background: var(--sidebar-bg-color);
} }
.leftCol.collapsed {
transform: translateX(-105%);
box-shadow: none;
pointer-events: none;
}
.leftCol-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
border-bottom: 1px solid var(--border-color);
background: var(--paper-bg-alt);
}
.leftCol-eyebrow {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-muted);
}
.leftCol-close {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--ink-muted);
cursor: pointer;
}
.leftCol-close:hover { color: var(--color); border-color: var(--color); }
.leftCol-foot { display: block; }
.leftCol-body { padding: 4px 0; }
.leftCol-backdrop {
display: block;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.32);
z-index: 1150;
backdrop-filter: blur(2px);
}
.dark-mode .leftCol-backdrop { background: rgba(0,0,0,0.5); }
} }
/* ===== Admin Interface (Paper style) ===== */ /* ===== Admin Interface (Paper style) ===== */
+39 -25
View File
@@ -1,64 +1,77 @@
<div class="container-fluid h-100"> <div class="explorer-page" ng-init="sidebarCollapsed = window.matchMedia && window.matchMedia('(max-width: 767px)').matches">
<div class="row h-100">
<button <button
class="sidebar-toggle" class="sidebar-toggle"
ng-show="files.length" ng-show="files.length"
ng-click="sidebarCollapsed = !sidebarCollapsed" ng-click="sidebarCollapsed = !sidebarCollapsed"
aria-label="{{sidebarCollapsed ? 'Show files' : 'Hide files'}}"
> >
<i class="fas" ng-class="sidebarCollapsed ? 'fa-folder-open' : 'fa-times'"></i> <i class="fas" ng-class="sidebarCollapsed ? 'fa-folder-open' : 'fa-times'"></i>
{{sidebarCollapsed ? 'Show Files' : 'Hide Files'}} <span ng-bind="sidebarCollapsed ? 'Files' : 'Close'"></span>
</button> </button>
<div
class="leftCol shadow p-1 overflow-auto" <div class="leftCol" ng-show="files.length" ng-class="{'collapsed': sidebarCollapsed}">
ng-show="files.length" <div class="leftCol-head">
ng-class="{'collapsed': sidebarCollapsed}" <span class="leftCol-eyebrow">Files</span>
> <button class="leftCol-close" ng-click="sidebarCollapsed = true" aria-label="Close files">
<i class="fas fa-times"></i>
</button>
</div>
<div class="leftCol-body">
<div <div
ng-if="options.truncatedFolders.length > 0" ng-if="options.truncatedFolders.length > 0"
class="alert alert-warning small p-2 mb-2" class="paper-inline-warning"
role="alert" role="alert"
> >
<i class="fas fa-exclamation-triangle"></i> <i class="fas fa-exclamation-triangle"></i>
{{ 'WARNINGS.repo_truncated' | translate }} {{ 'WARNINGS.repo_truncated' | translate }}
</div> </div>
<tree class="files" file="files"></tree> <tree class="files" file="files"></tree>
<div class="bottom column"> </div>
<div <div class="leftCol-foot">
<span
class="last-update" class="last-update"
data-toggle="tooltip" data-toggle="tooltip"
data-placement="top" data-placement="top"
title="{{options.lastUpdateDate}}" title="{{options.lastUpdateDate}}"
> >
Last Update: {{options.lastUpdateDate|date}} Updated {{options.lastUpdateDate|date}}
</span>
</div> </div>
</div> </div>
</div>
<div class="col-md h-100 overflow-auto p-0 d-flex flex-column"> <div
<div class="d-flex align-content-between status-bar shadow"> class="leftCol-backdrop"
<ol class="flex-grow-1 breadcrumb paths"> ng-show="files.length && !sidebarCollapsed"
<li class="breadcrumb-item" ng-repeat="p in paths" ng-bind="p"> ng-click="sidebarCollapsed = true"
Loading... ></div>
</li>
<div class="explorer-main">
<div class="status-bar">
<ol class="breadcrumb paths" aria-label="Path">
<li class="breadcrumb-item" ng-repeat="p in paths" ng-bind="p"></li>
</ol> </ol>
<div class="d-flex flex-wrap" style="gap: 4px"> <div class="status-bar-actions">
<a <a
ng-if="options.isAdmin || options.isOwner" ng-if="options.isAdmin || options.isOwner"
ng-href="/anonymize/{{repoId}}" ng-href="/anonymize/{{repoId}}"
class="btn btn-sm" class="btn btn-sm"
>Edit</a aria-label="Edit"
><i class="far fa-edit"></i><span class="d-none d-md-inline"> Edit</span></a
> >
<a <a
ng-show="content != null" ng-show="content != null"
ng-href="{{url}}" ng-href="{{url}}"
target="__self" target="__self"
class="btn btn-sm" class="btn btn-sm"
>Raw</a aria-label="Raw"
><i class="fas fa-file-alt"></i><span class="d-none d-md-inline"> Raw</span></a
> >
<a <a
ng-show="content != null" ng-show="content != null"
ng-href="{{url}}&download=true" ng-href="{{url}}&download=true"
target="__self" target="__self"
class="btn btn-sm" class="btn btn-sm"
aria-label="Download"
><i class="fas fa-download"></i><span class="d-none d-md-inline"> Download</span></a ><i class="fas fa-download"></i><span class="d-none d-md-inline"> Download</span></a
> >
<a <a
@@ -66,6 +79,7 @@
ng-href="/api/repo/{{repoId}}/zip" ng-href="/api/repo/{{repoId}}/zip"
target="__self" target="__self"
class="btn btn-sm" class="btn btn-sm"
aria-label="Download ZIP"
><i class="fas fa-file-archive"></i><span class="d-none d-md-inline"> ZIP</span></a ><i class="fas fa-file-archive"></i><span class="d-none d-md-inline"> ZIP</span></a
> >
<a <a
@@ -73,13 +87,13 @@
ng-href="/w/{{repoId}}/" ng-href="/w/{{repoId}}/"
target="__self" target="__self"
class="btn btn-sm" class="btn btn-sm"
>Website</a aria-label="Website"
><i class="fas fa-globe"></i><span class="d-none d-md-inline"> Website</span></a
> >
</div> </div>
</div> </div>
<div class="align-items-stretch h-100 w-100 overflow-auto"> <div class="explorer-content">
<ng-include src="'./partials/pageView.htm'"></ng-include> <ng-include src="'./partials/pageView.htm'"></ng-include>
</div> </div>
</div> </div>
</div>
</div> </div>
+58 -6
View File
@@ -18,7 +18,8 @@ angular
$scope.repositories = []; $scope.repositories = [];
$scope.total = -1; $scope.total = -1;
$scope.totalPage = 0; $scope.totalPage = 0;
$scope.query = { const reposAdminPrefsKey = "admin.repos.filterPrefs";
const reposAdminDefaults = {
page: 1, page: 1,
limit: 25, limit: 25,
sort: "lastView", sort: "lastView",
@@ -29,6 +30,11 @@ angular
error: true, error: true,
preparing: true, preparing: true,
}; };
const savedReposAdminPrefs = loadFilterPrefs(reposAdminPrefsKey) || {};
$scope.query = Object.assign({}, reposAdminDefaults, savedReposAdminPrefs, {
page: 1,
search: "",
});
$scope.removeCache = (repo) => { $scope.removeCache = (repo) => {
$http.delete("/api/admin/repos/" + repo.repoId).then( $http.delete("/api/admin/repos/" + repo.repoId).then(
@@ -85,6 +91,8 @@ angular
() => { () => {
clearTimeout(timeClear); clearTimeout(timeClear);
timeClear = setTimeout(getRepositories, 500); timeClear = setTimeout(getRepositories, 500);
const { page, search, ...persisted } = $scope.query;
saveFilterPrefs(reposAdminPrefsKey, persisted);
}, },
true true
); );
@@ -108,12 +116,18 @@ angular
$scope.users = []; $scope.users = [];
$scope.total = -1; $scope.total = -1;
$scope.totalPage = 0; $scope.totalPage = 0;
$scope.query = { const usersAdminPrefsKey = "admin.users.filterPrefs";
const usersAdminDefaults = {
page: 1, page: 1,
limit: 25, limit: 25,
sort: "username", sort: "username",
search: "", search: "",
}; };
const savedUsersAdminPrefs = loadFilterPrefs(usersAdminPrefsKey) || {};
$scope.query = Object.assign({}, usersAdminDefaults, savedUsersAdminPrefs, {
page: 1,
search: "",
});
function getUsers() { function getUsers() {
$http.get("/api/admin/users", { params: $scope.query }).then( $http.get("/api/admin/users", { params: $scope.query }).then(
@@ -136,6 +150,8 @@ angular
() => { () => {
clearTimeout(timeClear); clearTimeout(timeClear);
timeClear = setTimeout(getUsers, 500); timeClear = setTimeout(getUsers, 500);
const { page, search, ...persisted } = $scope.query;
saveFilterPrefs(usersAdminPrefsKey, persisted);
}, },
true true
); );
@@ -159,10 +175,38 @@ angular
$scope.userInfo; $scope.userInfo;
$scope.repositories = []; $scope.repositories = [];
$scope.search = ""; $scope.search = "";
$scope.filters = {
status: { ready: true, expired: false, removed: false }, const adminUserPrefsKey = "admin.user.filterPrefs";
const adminUserDefaults = {
filters: { status: { ready: true, expired: false, removed: false } },
orderBy: "-anonymizeDate",
}; };
$scope.orderBy = "-anonymizeDate"; const savedAdminUserPrefs = loadFilterPrefs(adminUserPrefsKey) || {};
$scope.filters = {
status: Object.assign(
{},
adminUserDefaults.filters.status,
(savedAdminUserPrefs.filters && savedAdminUserPrefs.filters.status) || {}
),
};
$scope.orderBy = savedAdminUserPrefs.orderBy || adminUserDefaults.orderBy;
$scope.$watch("orderBy", () => {
saveFilterPrefs(adminUserPrefsKey, {
filters: $scope.filters,
orderBy: $scope.orderBy,
});
});
$scope.$watch(
"filters",
() => {
saveFilterPrefs(adminUserPrefsKey, {
filters: $scope.filters,
orderBy: $scope.orderBy,
});
},
true
);
$scope.repoFiler = (repo) => { $scope.repoFiler = (repo) => {
if ($scope.filters.status[repo.status] == false) return false; if ($scope.filters.status[repo.status] == false) return false;
@@ -311,12 +355,18 @@ angular
$scope.conferences = []; $scope.conferences = [];
$scope.total = -1; $scope.total = -1;
$scope.totalPage = 0; $scope.totalPage = 0;
$scope.query = { const confAdminPrefsKey = "admin.conferences.filterPrefs";
const confAdminDefaults = {
page: 1, page: 1,
limit: 25, limit: 25,
sort: "name", sort: "name",
search: "", search: "",
}; };
const savedConfAdminPrefs = loadFilterPrefs(confAdminPrefsKey) || {};
$scope.query = Object.assign({}, confAdminDefaults, savedConfAdminPrefs, {
page: 1,
search: "",
});
function getConferences() { function getConferences() {
$http.get("/api/admin/conferences", { params: $scope.query }).then( $http.get("/api/admin/conferences", { params: $scope.query }).then(
@@ -339,6 +389,8 @@ angular
() => { () => {
clearTimeout(timeClear); clearTimeout(timeClear);
timeClear = setTimeout(getConferences, 500); timeClear = setTimeout(getConferences, 500);
const { page, search, ...persisted } = $scope.query;
saveFilterPrefs(confAdminPrefsKey, persisted);
}, },
true true
); );
+69 -7
View File
@@ -861,11 +861,45 @@ angular
$scope.items = []; $scope.items = [];
$scope.search = ""; $scope.search = "";
$scope.typeFilter = "all";
$scope.filters = { const dashboardPrefsKey = "dashboard.filterPrefs";
status: { ready: true, expired: true, removed: false }, const dashboardPrefDefaults = {
typeFilter: "all",
filters: { status: { ready: true, expired: true, removed: false } },
orderBy: "-anonymizeDate",
}; };
$scope.orderBy = "-anonymizeDate"; const savedDashboardPrefs = loadFilterPrefs(dashboardPrefsKey) || {};
$scope.typeFilter = savedDashboardPrefs.typeFilter || dashboardPrefDefaults.typeFilter;
$scope.filters = {
status: Object.assign(
{},
dashboardPrefDefaults.filters.status,
(savedDashboardPrefs.filters && savedDashboardPrefs.filters.status) || {}
),
};
$scope.orderBy = savedDashboardPrefs.orderBy || dashboardPrefDefaults.orderBy;
$scope.$watchGroup(
["typeFilter", "orderBy"],
() => {
saveFilterPrefs(dashboardPrefsKey, {
typeFilter: $scope.typeFilter,
filters: $scope.filters,
orderBy: $scope.orderBy,
});
}
);
$scope.$watch(
"filters",
() => {
saveFilterPrefs(dashboardPrefsKey, {
typeFilter: $scope.typeFilter,
filters: $scope.filters,
orderBy: $scope.orderBy,
});
},
true
);
function getQuota() { function getQuota() {
$http.get("/api/user/quota").then((res) => { $http.get("/api/user/quota").then((res) => {
@@ -2030,10 +2064,38 @@ angular
$scope.conferences = []; $scope.conferences = [];
$scope.search = ""; $scope.search = "";
$scope.filters = {
status: { ready: true, expired: false, removed: false }, const conferencesPrefsKey = "conferences.filterPrefs";
const conferencesPrefDefaults = {
filters: { status: { ready: true, expired: false, removed: false } },
orderBy: "name",
}; };
$scope.orderBy = "name"; const savedConferencesPrefs = loadFilterPrefs(conferencesPrefsKey) || {};
$scope.filters = {
status: Object.assign(
{},
conferencesPrefDefaults.filters.status,
(savedConferencesPrefs.filters && savedConferencesPrefs.filters.status) || {}
),
};
$scope.orderBy = savedConferencesPrefs.orderBy || conferencesPrefDefaults.orderBy;
$scope.$watch("orderBy", () => {
saveFilterPrefs(conferencesPrefsKey, {
filters: $scope.filters,
orderBy: $scope.orderBy,
});
});
$scope.$watch(
"filters",
() => {
saveFilterPrefs(conferencesPrefsKey, {
filters: $scope.filters,
orderBy: $scope.orderBy,
});
},
true
);
$scope.removeConference = function (conf) { $scope.removeConference = function (conf) {
if ( if (
+17
View File
@@ -1,3 +1,20 @@
function loadFilterPrefs(key) {
try {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : null;
} catch (e) {
return null;
}
}
function saveFilterPrefs(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (e) {
/* localStorage unavailable or quota exceeded */
}
}
function humanFileSize(bytes, si = false, dp = 1) { function humanFileSize(bytes, si = false, dp = 1) {
const thresh = si ? 1000 : 1024; const thresh = si ? 1000 : 1024;