Records on my fingers
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠙⠛⠛⠛⠿⠻⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⡀⠠⠤⠒⢂⣉⣉⣉⣑⣒⣒⠒⠒⠒⠒⠒⠒⠒⠀⠀⠐⠒⠚⠻⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀⡠⠔⠉⣀⠔⠒⠉⣀⣀⠀⠀⠀⣀⡀⠈⠉⠑⠒⠒⠒⠒⠒⠈⠉⠉⠉⠁⠂⠀⠈⠙⢿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠔⠁⠠⠖⠡⠔⠊⠀⠀⠀⠀⠀⠀⠀⠐⡄⠀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠉⠲⢄⠀⠀⠀⠈⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⠊⠀⢀⣀⣤⣤⣤⣤⣀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠜⠀⠀⠀⠀⣀⡀⠀⠈⠃⠀⠀⠀⠸⣿⣿⣿⣿ ⣿⣿⣿⣿⡿⠥⠐⠂⠀⠀⠀⠀⡄⠀⠰⢺⣿⣿⣿⣿⣿⣟⠀⠈⠐⢤⠀⠀⠀⠀⠀⠀⢀⣠⣶⣾⣯⠀⠀⠉⠂⠀⠠⠤⢄⣀⠙⢿⣿⣿ ⣿⡿⠋⠡⠐⠈⣉⠭⠤⠤⢄⡀⠈⠀⠈⠁⠉⠁⡠⠀⠀⠀⠉⠐⠠⠔⠀⠀⠀⠀⠀⠲⣿⠿⠛⠛⠓⠒⠂⠀⠀⠀⠀⠀⠀⠠⡉⢢⠙⣿ ⣿⠀⢀⠁⠀⠊⠀⠀⠀⠀⠀⠈⠁⠒⠂⠀⠒⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⢀⣀⡠⠔⠒⠒⠂⠀⠈⠀⡇⣿ ⣿⠀⢸⠀⠀⠀⢀⣀⡠⠋⠓⠤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠄⠀⠀⠀⠀⠀⠀⠈⠢⠤⡀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⡠⠀⡇⣿ ⣿⡀⠘⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠈⠑⡦⢄⣀⠀⠀⠐⠒⠁⢸⠀⠀⠠⠒⠄⠀⠀⠀⠀⠀⢀⠇⠀⣀⡀⠀⠀⢀⢾⡆⠀⠈⡀⠎⣸⣿ ⣿⣿⣄⡈⠢⠀⠀⠀⠀⠘⣶⣄⡀⠀⠀⡇⠀⠀⠈⠉⠒⠢⡤⣀⡀⠀⠀⠀⠀⠀⠐⠦⠤⠒⠁⠀⠀⠀⠀⣀⢴⠁⠀⢷⠀⠀⠀⢰⣿⣿ ⣿⣿⣿⣿⣇⠂⠀⠀⠀⠀⠈⢂⠀⠈⠹⡧⣀⠀⠀⠀⠀⠀⡇⠀⠀⠉⠉⠉⢱⠒⠒⠒⠒⢖⠒⠒⠂⠙⠏⠀⠘⡀⠀⢸⠀⠀⠀⣿⣿⣿ ⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠑⠄⠰⠀⠀⠁⠐⠲⣤⣴⣄⡀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢸⠀⠀⠀⠀⢠⠀⣠⣷⣶⣿⠀⠀⢰⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠁⢀⠀⠀⠀⠀⠀⡙⠋⠙⠓⠲⢤⣤⣷⣤⣤⣤⣤⣾⣦⣤⣤⣶⣿⣿⣿⣿⡟⢹⠀⠀⢸⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀⠑⠀⢄⠀⡰⠁⠀⠀⠀⠀⠀⠈⠉⠁⠈⠉⠻⠋⠉⠛⢛⠉⠉⢹⠁⢀⢇⠎⠀⠀⢸⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣀⠈⠢⢄⡉⠂⠄⡀⠀⠈⠒⠢⠄⠀⢀⣀⣀⣰⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⢀⣎⠀⠼⠊⠀⠀⠀⠘⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⡀⠉⠢⢄⡈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁⠀⠀⢀⠀⠀⠀⠀⠀⢻⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣀⡈⠑⠢⢄⡀⠈⠑⠒⠤⠄⣀⣀⠀⠉⠉⠉⠉⠀⠀⠀⣀⡀⠤⠂⠁⠀⢀⠆⠀⠀⢸⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⣄⡀⠁⠉⠒⠂⠤⠤⣀⣀⣉⡉⠉⠉⠉⠉⢀⣀⣀⡠⠤⠒⠈⠀⠀⠀⠀⣸⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣶⣤⣤⣤⣤⣀⣀⣤⣤⣤⣶⣾⣿⣿⣿⣿⣿
This commit is contained in:
parent
18251d7f00
commit
22ad0a0a11
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, title FROM paste",
|
||||
"query": "SELECT id, title, content FROM paste",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -12,15 +12,21 @@
|
|||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "content",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "218cab8328020896b78a81be345a2074ca5461affae6b7f4daa07ba6618f96aa"
|
||||
"hash": "118b5224bd11d8a0e9ab6e2c3b44ec4fc98990bd7fe434966fcd2e3580901f70"
|
||||
}
|
|
@ -5,9 +5,10 @@ use axum::{
|
|||
routing::get,
|
||||
Form, Router,
|
||||
};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use maud::{html, Markup};
|
||||
use serde::Deserialize;
|
||||
use sqlx::query;
|
||||
use sqlx::{query, query_as};
|
||||
use ulid::Ulid;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -28,38 +29,71 @@ pub(super) fn router() -> Router<SharedState> {
|
|||
|
||||
const BC_PASTES: Breadcrumb = Breadcrumb::new_static("Pastes", "/admin/pastes");
|
||||
|
||||
// async fn show_pastes(
|
||||
// State(SharedState { db, .. }): State<SharedState>,
|
||||
// ) -> Result<Markup, AppError> {
|
||||
// Ok(page(
|
||||
// "Pastes",
|
||||
// &[BC_INDEX, BC_ADMIN],
|
||||
// html! {
|
||||
// table class="w-full" {
|
||||
// thead {
|
||||
// tr {
|
||||
// th { "Name" }
|
||||
// th { "Created at" }
|
||||
// th { "Actions" }
|
||||
// }
|
||||
// }
|
||||
// tbody {
|
||||
// @for p in query!("SELECT id, title FROM
|
||||
// paste").fetch_all(&db).await? { tr {
|
||||
// td { a.tl href=(format!("/p/{}",
|
||||
// Ulid::from(p.id))) { (p.title)} } td {
|
||||
// (dt_iso(Ulid::from(p.id).datetime())) } td
|
||||
// class="w-min-content" { div class="flex
|
||||
// gap-2" { a.tl href="#" { "Edit" }
|
||||
// a.tl href="#" { "Delete" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// ))
|
||||
// }
|
||||
|
||||
struct PasteInList {
|
||||
id: Ulid,
|
||||
title: String,
|
||||
content: String,
|
||||
timestamp: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "admin/pastes.html")]
|
||||
struct ShowPastesTemplate {
|
||||
pastes: Vec<PasteInList>,
|
||||
}
|
||||
|
||||
async fn show_pastes(
|
||||
State(SharedState { db, .. }): State<SharedState>,
|
||||
) -> Result<Markup, AppError> {
|
||||
Ok(page(
|
||||
"Pastes",
|
||||
&[BC_INDEX, BC_ADMIN],
|
||||
html! {
|
||||
table class="w-full" {
|
||||
thead {
|
||||
tr {
|
||||
th { "Name" }
|
||||
th { "Created at" }
|
||||
th { "Actions" }
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
@for p in query!("SELECT id, title FROM paste").fetch_all(&db).await? {
|
||||
tr {
|
||||
td { a.tl href=(format!("/p/{}", Ulid::from(p.id))) { (p.title)} }
|
||||
td { (dt_iso(Ulid::from(p.id).datetime())) }
|
||||
td class="w-min-content" {
|
||||
div class="flex gap-2" {
|
||||
a.tl href="#" { "Edit" }
|
||||
a.tl href="#" { "Delete" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
))
|
||||
) -> Result<ShowPastesTemplate, AppError> {
|
||||
let pastes = query!("SELECT id, title, content FROM paste")
|
||||
.fetch_all(&db)
|
||||
.await?;
|
||||
let pastes = pastes
|
||||
.into_iter()
|
||||
.map(|r| PasteInList {
|
||||
id: r.id.into(),
|
||||
title: r.title,
|
||||
content: r.content,
|
||||
timestamp: DateTime::from_timestamp_millis(Ulid::from(r.id).timestamp_ms() as i64)
|
||||
.map(|d| d.to_rfc3339())
|
||||
.unwrap_or("<invalid timestamp>".to_string()),
|
||||
})
|
||||
.collect();
|
||||
Ok(ShowPastesTemplate { pastes })
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
|
|
1
static/.map
Normal file
1
static/.map
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["styles/tailwind/_border-radius.scss","styles/tailwind/_colors.scss","styles/tailwind/_spacing.scss","styles/_theme.scss","styles/style.scss","styles/tailwind/_text.scss"],"names":[],"mappings":"AAAA;ACAA;ACAA;AAAA;AAAA;AAOA;ACDA;EACE;;;AAGF;AACA;AACE;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;;AAEA;EAfF;AAgBI;IACA;IACA;IACA;AAEA;IACA;IACA;IACA;AAEA;IACA;IACA;;;;AClCJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA,WFEY;;;AEChB;EAEI;;AAEA;EACI,WFJQ;;AEOZ;EARJ;IASQ;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA,WC7BI;ED8BJ,aCDY;;ADKpB;EACI;EACA;EACA;;;AAKJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAEA;AAAA;EAEI;EACA;EACA;EACA,eJ1DR;EI2DQ;EACA;EACA,WCjED;EDkEC;;AAEA;AAAA;AAAA;EAEI;EACA;;AAOZ;EAEI;EACA;EACA;EACA,eJ9EJ;EI+EI","file":"static"}
|
179
static/admin.css
Normal file
179
static/admin.css
Normal file
|
@ -0,0 +1,179 @@
|
|||
/* https://github.com/tailwindlabs/tailwindcss/blob/ac6d4a6e8e9ae7ca197bf98d933ae2f205be3635/packages/tailwindcss/src/compat/default-theme.ts#L162 */
|
||||
/* Tailwind palette */
|
||||
/* Yes, it's that simple.
|
||||
* https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale
|
||||
* https://github.com/tailwindlabs/tailwindcss/blob/ac6d4a6e8e9ae7ca197bf98d933ae2f205be3635/packages/tailwindcss/src/compat/default-theme.ts#L909 */
|
||||
/* https://github.com/tailwindlabs/tailwindcss/blob/ac6d4a6e8e9ae7ca197bf98d933ae2f205be3635/packages/tailwindcss/src/compat/default-theme.ts#L715 */
|
||||
html {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
/* These need switchable client-side */
|
||||
:root {
|
||||
/* Foreground */
|
||||
--color-fg-base: #334155;
|
||||
--color-fg-deemphasized: #475569;
|
||||
--color-fg-headings: #0f172a;
|
||||
/* Background */
|
||||
--color-bg-base: #f1f5f9;
|
||||
--color-bg-raised-1: #e2e8f0;
|
||||
--color-bg-raised-2: #cbd5e1;
|
||||
--color-bg-raised-3: #94a3b8;
|
||||
/* Borders */
|
||||
--color-bd-base: #cbd5e1;
|
||||
--color-bd-highlighted-1: #64748b;
|
||||
--color-bd-highlighted-2: #475569;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
/* Foreground */
|
||||
--color-fg-base: #cbd5e1;
|
||||
--color-fg-deemphasized: #94a3b8;
|
||||
--color-fg-headings: #f1f5f9;
|
||||
/* Background */
|
||||
--color-bg-base: #0f172a;
|
||||
--color-bg-raised-1: #1e293b;
|
||||
--color-bg-raised-2: #334155;
|
||||
/* Borders */
|
||||
--color-bd-base: #334155;
|
||||
--color-bd-highlighted: #64748b;
|
||||
}
|
||||
}
|
||||
|
||||
body.admin {
|
||||
width: 100%;
|
||||
max-width: 64rem;
|
||||
padding: 4rem 2rem;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 16rem auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-areas: "logo none" "sidebar main" "footer footer";
|
||||
gap: 1rem;
|
||||
}
|
||||
body.admin > h1 {
|
||||
color: var(--color-fg-headings);
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
font-size: 2.25rem;
|
||||
grid-area: logo;
|
||||
}
|
||||
body.admin > aside[role=navigation] {
|
||||
grid-area: sidebar;
|
||||
}
|
||||
body.admin > aside[role=navigation] ul {
|
||||
list-style: none;
|
||||
margin: unset;
|
||||
padding: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
body.admin > aside[role=navigation] a {
|
||||
display: block;
|
||||
padding: 0.75rem 1rem;
|
||||
color: unset;
|
||||
text-decoration: unset;
|
||||
border: 1px solid var(--color-bd-base);
|
||||
border-radius: 0.75rem;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-property: background-color, border-color;
|
||||
background-color: unset;
|
||||
cursor: pointer;
|
||||
}
|
||||
body.admin > aside[role=navigation] a:hover, body.admin > aside[role=navigation] a:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
background-color: var(--color-bg-raised-1);
|
||||
}
|
||||
body.admin > aside[role=navigation] a.active- {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
background-color: var(--color-bg-raised-1);
|
||||
}
|
||||
body.admin > aside[role=navigation] a.active-:hover, body.admin > aside[role=navigation] a.active-:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-2);
|
||||
background-color: var(--color-bg-raised-2);
|
||||
}
|
||||
body.admin > main {
|
||||
grid-area: main;
|
||||
}
|
||||
body.admin > footer {
|
||||
grid-area: footer;
|
||||
color: var(--color-fg-deemphasized);
|
||||
}
|
||||
|
||||
body.admin-pastes main > ul {
|
||||
list-style: none;
|
||||
margin: unset;
|
||||
padding: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
body.admin-pastes main > ul li {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-areas: "title buttons" "timestamp buttons" "content content";
|
||||
gap: 1rem;
|
||||
}
|
||||
body.admin-pastes main > ul li .title {
|
||||
grid-area: title;
|
||||
color: var(--color-fg-headings);
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
body.admin-pastes main > ul li .title:hover, body.admin-pastes main > ul li .title:focus-visible {
|
||||
text-decoration: underline;
|
||||
}
|
||||
body.admin-pastes main > ul li time {
|
||||
grid-area: timestamp;
|
||||
color: var(--color-fg-deemphasized);
|
||||
}
|
||||
body.admin-pastes main > ul li .action-buttons {
|
||||
grid-area: buttons;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
body.admin-pastes main > ul li .action-buttons button {
|
||||
border: 1px solid var(--color-bd-base);
|
||||
border-radius: 0.75rem;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-property: background-color, border-color;
|
||||
background-color: var(--color-bg-raised-1);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: unset;
|
||||
}
|
||||
body.admin-pastes main > ul li .action-buttons button:hover, body.admin-pastes main > ul li .action-buttons button:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
background-color: var(--color-bg-raised-2);
|
||||
}
|
||||
body.admin-pastes main > ul li .action-buttons button.active- {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
background-color: var(--color-bg-raised-2);
|
||||
}
|
||||
body.admin-pastes main > ul li .action-buttons button.active-:hover, body.admin-pastes main > ul li .action-buttons button.active-:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-2);
|
||||
background-color: var(--color-bg-raised-3);
|
||||
}
|
||||
body.admin-pastes main > ul li .content {
|
||||
grid-area: content;
|
||||
border: 1px solid var(--color-bd-base);
|
||||
border-radius: 0.75rem;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-property: background-color, border-color;
|
||||
background-color: var(--color-bg-raised-1);
|
||||
padding: 1rem;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=admin.css.map */
|
1
static/admin.css.map
Normal file
1
static/admin.css.map
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../styles/tailwind/_border-radius.scss","../styles/tailwind/_colors.scss","../styles/tailwind/_spacing.scss","../styles/_theme.scss","../styles/admin.scss","../styles/tailwind/_text.scss","../styles/tailwind/_transition.scss"],"names":[],"mappings":"AAAA;ACAA;ACAA;AAAA;AAAA;AAOA;ACDA;EACI;;;AAGJ;AACA;AACI;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;;AAEA;EAjBJ;AAkBQ;IACA;IACA;IACA;AAEA;IACA;IACA;IACA;AAEA;IACA;IACA;;;;ACtCR;EACI;EACA,WFYY;EEXZ;EACA;EACA;EACA;EACA;EACA,qBACI;EAGJ;;AAEA;ED6BA;EACA,aEbe;EFeX;EAMJ,WEhDY;EDYR;;AAGJ;EACI;;AAEA;EACI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EACI;EACA;EACA;EACA;ED+BR;EACA,eHhEI;EGiEJ,qBGjEe;EHkEf,4BGxEgB;EHyEhB;EAKI;EAIA;;AAEA;EAEI;EAII;;AAIR;EACI;EAII;;AAGJ;EAEI;EAII;;ACjEhB;EACI;;AAGJ;EACI;EACA;;;AAKJ;EACI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI;EACA;EACA;EACA,qBACI;EAGJ;;AAEA;EACI;ED3BZ;EACA,aEbe;EFeX;EAgBJ,WE5DY;EDsEA;;AAEA;EAEI;;AAIR;EACI;EACA;;AAGJ;EACI;EACA;EACA;EACA;EACA;;AAEA;ED1BZ;EACA,eHhEI;EGiEJ,qBGjEe;EHkEf,4BGxEgB;EHyEhB;EAGI;EAMA;EAmCJ;EACA;EACA,WEtHW;EFuHX;;AApCI;EAEI;EAEI;;AAMR;EACI;EAEI;;AAKJ;EAEI;EAEI;;ACNR;EACI;EDhCZ;EACA,eHhEI;EGiEJ,qBGjEe;EHkEf,4BGxEgB;EHyEhB;EAGI;EC2BQ;EACA","file":"admin.css"}
|
|
@ -1,3 +1,4 @@
|
|||
/* https://github.com/tailwindlabs/tailwindcss/blob/ac6d4a6e8e9ae7ca197bf98d933ae2f205be3635/packages/tailwindcss/src/compat/default-theme.ts#L162 */
|
||||
/* Tailwind palette */
|
||||
/* Yes, it's that simple.
|
||||
* https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale
|
||||
|
@ -17,9 +18,11 @@ html {
|
|||
--color-bg-base: #f1f5f9;
|
||||
--color-bg-raised-1: #e2e8f0;
|
||||
--color-bg-raised-2: #cbd5e1;
|
||||
--color-bg-raised-3: #94a3b8;
|
||||
/* Borders */
|
||||
--color-bd-base: #cbd5e1;
|
||||
--color-bd-highlighted: #64748b;
|
||||
--color-bd-highlighted-1: #64748b;
|
||||
--color-bd-highlighted-2: #475569;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
|
@ -37,7 +40,6 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
/* https://github.com/tailwindlabs/tailwindcss/blob/ac6d4a6e8e9ae7ca197bf98d933ae2f205be3635/packages/tailwindcss/src/compat/default-theme.ts#L162 */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -46,28 +48,31 @@ body {
|
|||
font-family: Inter, "Inter Variable", sans-serif;
|
||||
color: var(--color-fg-base);
|
||||
background-color: var(--color-bg-base);
|
||||
padding: 4rem 2rem;
|
||||
margin: 0 auto;
|
||||
max-width: 56rem;
|
||||
}
|
||||
body.wide {
|
||||
|
||||
body:not(.admin) {
|
||||
max-width: 56rem;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
body:not(.admin).wide {
|
||||
max-width: 72rem;
|
||||
}
|
||||
@media not all and (min-width: var(--max-width-lg)) {
|
||||
body {
|
||||
body:not(.admin) {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
body header {
|
||||
body:not(.admin) header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
body header h1 {
|
||||
body:not(.admin) header h1 {
|
||||
margin: 0;
|
||||
color: var(--color-fg-headings);
|
||||
font-size: 1.875rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
body footer {
|
||||
body:not(.admin) footer {
|
||||
color: var(--color-fg-deemphasized);
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
|
@ -108,35 +113,4 @@ body.paste main pre {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
body.admin-pastes main form input[type=text],
|
||||
body.admin-pastes main form textarea,
|
||||
body.admin-pastes main form input[type=submit] {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--color-bg-raised-1);
|
||||
border: 1px solid var(--color-bd-base);
|
||||
font-size: inherit;
|
||||
}
|
||||
body.admin-pastes main form input[type=text]:hover, body.admin-pastes main form input[type=text]:focus-visible,
|
||||
body.admin-pastes main form textarea:hover,
|
||||
body.admin-pastes main form textarea:focus-visible,
|
||||
body.admin-pastes main form input[type=submit]:hover,
|
||||
body.admin-pastes main form input[type=submit]:focus-visible {
|
||||
background-color: var(--color-bg-raised-2);
|
||||
border-color: var(--color-bd-highlighted);
|
||||
}
|
||||
body.admin-pastes main form input[type=text],
|
||||
body.admin-pastes main form textarea {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
body.admin-pastes main form textarea {
|
||||
min-height: 8rem;
|
||||
}
|
||||
body.admin-pastes main form input[type=submit] {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=style.css.map */
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../styles/tailwind/_colors.scss","../styles/tailwind/_spacing.scss","../styles/_theme.scss","../styles/tailwind/_border-radius.scss","../styles/style.scss","../styles/tailwind/_text.scss"],"names":[],"mappings":"AAAA;ACAA;AAAA;AAAA;AAOA;ACHA;EACE;;;AAGF;AACA;AACE;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;;AAEA;EAfF;AAgBI;IACA;IACA;IACA;AAEA;IACA;IACA;IACA;AAEA;IACA;IACA;;;;ACrCJ;ACKA;EACI;;;AAGJ;EAEI;EACA;EACA;EACA;EACA;EACA;;AAEA;EACI,WHDQ;;AGIZ;EAbJ;IAcQ;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA,WC1BI;ED2BJ,aCEY;;ADEpB;EACI;EACA;EACA;;;AAKJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAEA;AAAA;EAEI;EACA;EACA;EACA,eDvDR;ECwDQ;EACA;EACA,WC9DD;ED+DC;;AAEA;AAAA;AAAA;EAEI;EACA;;AAOZ;EAEI;EACA;EACA;EACA,eD3EJ;EC4EI;;;AAOJ;AAAA;AAAA;EAGI;EACA,eDxFJ;ECyFI;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;EAEI;EACA;;AAIR;AAAA;EAEI;EACA;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA","file":"style.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["../styles/tailwind/_border-radius.scss","../styles/tailwind/_colors.scss","../styles/tailwind/_spacing.scss","../styles/_theme.scss","../styles/style.scss","../styles/tailwind/_text.scss"],"names":[],"mappings":"AAAA;ACAA;ACAA;AAAA;AAAA;AAOA;ACDA;EACI;;;AAGJ;AACA;AACI;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;;AAEA;EAjBJ;AAkBQ;IACA;IACA;IACA;AAEA;IACA;IACA;IACA;AAEA;IACA;IACA;;;;ACpCR;EACI;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EAEI,WFFY;EEGZ;;AAEA;EACI,WFJQ;;AEOZ;EATJ;IAUQ;;;AAGJ;EACI;;AAEA;EACI;EACA;EACA,WC7BI;ED8BJ,aCDY;;ADKpB;EACI;EACA;EACA;;;AAKJ;EACI;EACA;EACA;;AAEA;EACI;EACA;EACA;;AAEA;AAAA;EAEI;EACA;EACA;EACA,eJ1DR;EI2DQ;EACA;EACA,WCjED;EDkEC;;AAEA;AAAA;AAAA;EAEI;EACA;;AAOZ;EAEI;EACA;EACA;EACA,eJ9EJ;EI+EI","file":"style.css"}
|
|
@ -1,5 +1,7 @@
|
|||
@use "tailwind/border-radius";
|
||||
@use "tailwind/colors";
|
||||
@use "tailwind/spacing";
|
||||
@use "tailwind/transition";
|
||||
@use "tailwind/text";
|
||||
|
||||
html {
|
||||
|
@ -17,10 +19,12 @@ html {
|
|||
--color-bg-base: #{colors.$slate-100};
|
||||
--color-bg-raised-1: #{colors.$slate-200};
|
||||
--color-bg-raised-2: #{colors.$slate-300};
|
||||
--color-bg-raised-3: #{colors.$slate-400};
|
||||
|
||||
/* Borders */
|
||||
--color-bd-base: #{colors.$slate-300};
|
||||
--color-bd-highlighted: #{colors.$slate-500};
|
||||
--color-bd-highlighted-1: #{colors.$slate-500};
|
||||
--color-bd-highlighted-2: #{colors.$slate-600};
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* Foreground */
|
||||
|
@ -41,6 +45,7 @@ html {
|
|||
|
||||
@mixin heading($disable-margin: false) {
|
||||
color: var(--color-fg-headings);
|
||||
font-weight: text.$font-weight-bold;
|
||||
@if $disable-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -49,5 +54,69 @@ html {
|
|||
@mixin heading2($disable-margin: false) {
|
||||
@include heading($disable-margin);
|
||||
font-size: text.$font-size-4xl;
|
||||
font-weight: text.$font-weight-bold;
|
||||
}
|
||||
|
||||
@mixin heading3($disable-margin: false) {
|
||||
@include heading($disable-margin);
|
||||
font-size: text.$font-size-3xl;
|
||||
}
|
||||
|
||||
@mixin heading4($disable-margin: false) {
|
||||
@include heading($disable-margin);
|
||||
font-size: text.$font-size-2xl;
|
||||
}
|
||||
|
||||
@mixin surface($is-interactive: false, $is-raised: false) {
|
||||
border: 1px solid var(--color-bd-base);
|
||||
border-radius: border-radius.$br-xl;
|
||||
transition-duration: transition.$duration-default;
|
||||
transition-timing-function: transition.$timing-fn-default;
|
||||
transition-property: background-color, border-color;
|
||||
|
||||
@if $is-raised {
|
||||
background-color: var(--color-bg-raised-1);
|
||||
} @else {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
@if $is-interactive {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
@if $is-raised {
|
||||
background-color: var(--color-bg-raised-2);
|
||||
} @else {
|
||||
background-color: var(--color-bg-raised-1);
|
||||
}
|
||||
}
|
||||
|
||||
&.active- {
|
||||
border-color: var(--color-bd-highlighted-1);
|
||||
@if $is-raised {
|
||||
background-color: var(--color-bg-raised-2);
|
||||
} @else {
|
||||
background-color: var(--color-bg-raised-1);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
border-color: var(--color-bd-highlighted-2);
|
||||
@if $is-raised {
|
||||
background-color: var(--color-bg-raised-3);
|
||||
} @else {
|
||||
background-color: var(--color-bg-raised-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button($is-raised: false) {
|
||||
@include surface(true, $is-raised);
|
||||
display: block;
|
||||
padding: spacing.s(1) spacing.s(3);
|
||||
font-size: text.$font-size-sm;
|
||||
color: unset;
|
||||
}
|
||||
|
|
109
styles/admin.scss
Normal file
109
styles/admin.scss
Normal file
|
@ -0,0 +1,109 @@
|
|||
@use "theme";
|
||||
@use "tailwind/spacing";
|
||||
|
||||
body.admin {
|
||||
width: 100%;
|
||||
max-width: spacing.$max-width-5xl;
|
||||
padding: spacing.s(16) spacing.s(8);
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: spacing.s(64) auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-areas:
|
||||
"logo none"
|
||||
"sidebar main"
|
||||
"footer footer";
|
||||
gap: spacing.s(4);
|
||||
|
||||
> h1 {
|
||||
@include theme.heading2(true);
|
||||
grid-area: logo;
|
||||
}
|
||||
|
||||
> aside[role="navigation"] {
|
||||
grid-area: sidebar;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: unset;
|
||||
padding: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: spacing.s(2);
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: spacing.s(3) spacing.s(4);
|
||||
color: unset;
|
||||
text-decoration: unset;
|
||||
@include theme.surface(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
> main {
|
||||
grid-area: main;
|
||||
}
|
||||
|
||||
> footer {
|
||||
grid-area: footer;
|
||||
color: var(--color-fg-deemphasized);
|
||||
}
|
||||
}
|
||||
|
||||
body.admin-pastes {
|
||||
main > ul {
|
||||
list-style: none;
|
||||
margin: unset;
|
||||
padding: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: spacing.s(6);
|
||||
|
||||
li {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-template-rows: auto auto;
|
||||
grid-template-areas:
|
||||
"title buttons"
|
||||
"timestamp buttons"
|
||||
"content content";
|
||||
gap: spacing.s(4);
|
||||
|
||||
.title {
|
||||
grid-area: title;
|
||||
@include theme.heading4(true);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
time {
|
||||
grid-area: timestamp;
|
||||
color: var(--color-fg-deemphasized);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
grid-area: buttons;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: end;
|
||||
gap: spacing.s(2);
|
||||
|
||||
button {
|
||||
@include theme.button(true);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-area: content;
|
||||
@include theme.surface(false, true);
|
||||
padding: spacing.s(4);
|
||||
margin: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,13 +8,16 @@
|
|||
}
|
||||
|
||||
body {
|
||||
// @apply flex flex-col gap-8 px-4 py-16 mx-auto max-w-4xl bg-slate-900 text-fg-base max-lg:py-8;
|
||||
font-family: Inter, "Inter Variable", sans-serif;
|
||||
color: var(--color-fg-base);
|
||||
background-color: var(--color-bg-base);
|
||||
padding: spacing.s(16) spacing.s(8);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
body:not(.admin) {
|
||||
// @apply flex flex-col gap-8 px-4 py-16 mx-auto max-w-4xl bg-slate-900 text-fg-base max-lg:py-8;
|
||||
max-width: spacing.$max-width-4xl;
|
||||
padding: spacing.s(16) spacing.s(8);
|
||||
|
||||
&.wide {
|
||||
max-width: spacing.$max-width-6xl;
|
||||
|
@ -85,41 +88,41 @@ body.paste {
|
|||
}
|
||||
}
|
||||
|
||||
body.admin-pastes {
|
||||
main form {
|
||||
input[type="text"],
|
||||
textarea,
|
||||
input[type="submit"] {
|
||||
padding: spacing.s(2) spacing.s(3);
|
||||
border-radius: border-radius.$br-lg;
|
||||
background-color: var(--color-bg-raised-1);
|
||||
border: 1px solid var(--color-bd-base);
|
||||
font-size: inherit;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background-color: var(--color-bg-raised-2);
|
||||
border-color: var(--color-bd-highlighted);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
margin-bottom: spacing.s(4);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: spacing.s(32);
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
// body.admin-pastes {
|
||||
// main form {
|
||||
// input[type="text"],
|
||||
// textarea,
|
||||
// input[type="submit"] {
|
||||
// padding: spacing.s(2) spacing.s(3);
|
||||
// border-radius: border-radius.$br-lg;
|
||||
// background-color: var(--color-bg-raised-1);
|
||||
// border: 1px solid var(--color-bd-base);
|
||||
// font-size: inherit;
|
||||
//
|
||||
// &:hover,
|
||||
// &:focus-visible {
|
||||
// background-color: var(--color-bg-raised-2);
|
||||
// border-color: var(--color-bd-highlighted);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// input[type="text"],
|
||||
// textarea {
|
||||
// width: 100%;
|
||||
// margin-bottom: spacing.s(4);
|
||||
// }
|
||||
//
|
||||
// textarea {
|
||||
// min-height: spacing.s(32);
|
||||
// }
|
||||
//
|
||||
// input[type="submit"] {
|
||||
// cursor: pointer;
|
||||
// margin-left: auto;
|
||||
// display: block;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
a.tl {
|
||||
// @apply underline hover:no-underline hover:text-white focus-visible:no-underline focus-visible:text-white;
|
||||
|
|
7
styles/tailwind/_transition.scss
Normal file
7
styles/tailwind/_transition.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
$timing-fn-default: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
$timing-fn-linear: linear;
|
||||
$timing-fn-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
$timing-fn-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
$timing-fn-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
$duration-default: 150ms;
|
|
@ -1,12 +1,31 @@
|
|||
{% extends "../base.html" %}
|
||||
|
||||
{% block bodyClass %}admin{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<h1>{{title}}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{main}}
|
||||
{% call super() %}
|
||||
{% endblock %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% block title %}{{title}}{% endblock %} | NCPN</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<link rel="stylesheet" href="/admin.css" />
|
||||
<script src="/script.js" defer></script>
|
||||
<noscript>
|
||||
<style>
|
||||
.jsonly {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="admin {% block bodyClass %}{% endblock %}">
|
||||
<h1>NCPN Admin</h1>
|
||||
<aside role="navigation">
|
||||
<ul>
|
||||
<li><a href="/admin/files">Files</a></li>
|
||||
<li><a href="/admin/links">Links</a></li>
|
||||
<li><a class="active" href="/admin/pastes">Pastes</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<main>{% block main %}{% endblock %}</main>
|
||||
<footer>Running NCPN v?.??</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
{% extends "../base.html" %}
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Pastes{% endblock %}
|
||||
{% block bodyClass %}admin-pastes{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<h1>Pastes</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<ul>
|
||||
{% for paste in pastes %}
|
||||
<li>
|
||||
<a class="title" href="/p/{{ paste.id }}">{{ paste.title|e }}</a>
|
||||
<time datetime="{{ paste.timestamp }}">{{ paste.timestamp }}</time>
|
||||
<div class="action-buttons">
|
||||
<button>Edit</button>
|
||||
<button>Delete</button>
|
||||
</div>
|
||||
<pre class="content">{{ paste.content|e }}</pre>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% call super() %}
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue