From fbfc3d29704772788a82bb782ba94e58430c9982 Mon Sep 17 00:00:00 2001 From: Nicole Tietz-Sokolskaya Date: Fri, 17 May 2024 22:42:06 -0400 Subject: [PATCH] make it so we can save/load docs --- Cargo.lock | 1 + Cargo.toml | 2 +- frontend/main.ts | 55 +++++++++---------- src/handler/documents.rs | 48 +++++++++++++++- src/server.rs | 3 +- static/main.js | 35 ++++++------ static/style.css | 73 +++++++++++++++++++++++++ templates/documents/edit_document.html | 48 ++++++++++++++++ templates/documents/list_documents.html | 2 +- 9 files changed, 213 insertions(+), 54 deletions(-) create mode 100644 templates/documents/edit_document.html diff --git a/Cargo.lock b/Cargo.lock index 47e7508..b5f13e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1858,6 +1858,7 @@ dependencies = [ "memo-map", "self_cell", "serde", + "serde_json", "v_htmlescape", ] diff --git a/Cargo.toml b/Cargo.toml index 92cefe2..6308e34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ dotenvy = "0.15.7" env_logger = "0.11.3" fjall = "0.6.5" free-icons = "0.7.0" -minijinja = { version = "1.0.14", features = ["loader"] } +minijinja = { version = "1.0.14", features = ["loader", "json", "builtins"] } minijinja-autoreload = "1.0.14" model_derive = { path = "./model_derive" } rand = "0.8.5" diff --git a/frontend/main.ts b/frontend/main.ts index a36f1d4..83332f4 100644 --- a/frontend/main.ts +++ b/frontend/main.ts @@ -7,18 +7,6 @@ import { nord } from '@milkdown/theme-nord' import { listener, listenerCtx } from '@milkdown/plugin-listener'; import '@milkdown/theme-nord/style.css' -const markdown = -`# Milkdown Vanilla Commonmark - -> You're scared of a world where you're needed. - -This is a demo for using Milkdown with **Vanilla Typescript**. - -- A list -- [ ] Something to do -- [x] Something done` - - function configureListItem(ctx: Ctx) { ctx.set(listItemBlockConfig.key, { renderLabel: (label: string, listType, checked?: boolean) => { @@ -35,23 +23,30 @@ function configureListItem(ctx: Ctx) { }) } -Editor - .make() - .config(ctx => { - ctx.set(rootCtx, "#editor") - ctx.set(defaultValueCtx, markdown) +function createEditor(rootId, fieldId, content) { + Editor + .make() + .config(ctx => { + ctx.set(rootCtx, rootId) + ctx.set(defaultValueCtx, content) - const listener = ctx.get(listenerCtx); - listener.markdownUpdated((ctx, markdown, prevMarkdown) => { - if (markdown !== prevMarkdown) { - console.log(markdown); - } + const listener = ctx.get(listenerCtx); + listener.markdownUpdated((ctx, markdown, prevMarkdown) => { + if (markdown !== prevMarkdown) { + console.log(markdown); + console.log(fieldId); + document.getElementById(fieldId).value = markdown; + console.log("updated"); + } + }) }) - }) - .config(configureListItem) - .use(commonmark) - .use(gfm) - .use(nord) - .use(listener) - .use(listItemBlockComponent) - .create(); + .config(configureListItem) + .use(commonmark) + .use(gfm) + .use(nord) + .use(listener) + .use(listItemBlockComponent) + .create(); +} + +window.createEditor = createEditor; diff --git a/src/handler/documents.rs b/src/handler/documents.rs index e6908ee..52c7992 100644 --- a/src/handler/documents.rs +++ b/src/handler/documents.rs @@ -45,8 +45,8 @@ pub async fn create_document_page( #[derive(Debug, Deserialize)] pub struct CreateDocumentSubmission { - project_id: Uuid, - title: String, + pub project_id: Uuid, + pub title: String, } pub async fn create_document_submit( @@ -117,6 +117,7 @@ pub async fn edit_document_page( } }; + dbg!(&document); let values = context! { user => user, document => document, @@ -124,3 +125,46 @@ pub async fn edit_document_page( ctx.render_resp("documents/edit_document.html", values) } + +#[derive(Debug, Deserialize)] +pub struct EditDocumentSubmission { + pub title: String, + pub content: String, +} + + +pub async fn edit_document_submit( + State(ctx): State, + auth_session: AuthSession, + Path((document_id,)): Path<(Uuid,)>, + form: Form, +) -> Response { + let user = match auth_session.user { + Some(user) => user, + None => return Redirect::to("/login").into_response(), + }; + + let mut document = match ModelPermission::user_document(&ctx.kv_handles, user.id, document_id) { + Ok(Some(document)) => document, + Ok(None) => return Redirect::to("/documents").into_response(), + Err(err) => { + error!(?err, "failed to load document"); + return internal_server_error(); + } + }; + + let new_document = Document { + id: document.id, + project_id: document.id, + title: form.title.to_owned(), + content: form.content.to_owned(), + }; + + if let Err(err) = new_document.save(&ctx.kv_handles) { + error!(?err, "failed to save document"); + return internal_server_error(); + } + info!(?new_document, "document updated"); + + Redirect::to("/documents").into_response() +} diff --git a/src/server.rs b/src/server.rs index 309e23e..e6e96b8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,7 +10,7 @@ use tower_sessions::SessionManagerLayer; use tower_sessions_sqlx_store::{sqlx::SqlitePool, SqliteStore}; use tracing::Level; -use crate::{config::CommandLineOptions, context::Context, handler::{documents::{create_document_page, create_document_submit, documents_page, edit_document_page}, home::home_page, login::logout, login_page, login_submit, projects::{create_project_page, create_project_submit, projects_page}}, kv::KvHandle, logging::setup_logging, templates::make_template_loader}; +use crate::{config::CommandLineOptions, context::Context, handler::{documents::{create_document_page, create_document_submit, documents_page, edit_document_page, edit_document_submit}, home::home_page, login::logout, login_page, login_submit, projects::{create_project_page, create_project_submit, projects_page}}, kv::KvHandle, logging::setup_logging, templates::make_template_loader}; pub async fn run() -> Result<()> { dotenvy::dotenv()?; @@ -50,6 +50,7 @@ pub async fn run() -> Result<()> { .route("/documents/new", get(create_document_page)) .route("/documents/new", post(create_document_submit)) .route("/documents/edit/:id", get(edit_document_page)) + .route("/documents/edit/:id", post(edit_document_submit)) .layer(trace_layer) .layer(session_layer) .layer(auth_layer) diff --git a/static/main.js b/static/main.js index d7e98bb..a0e49f3 100644 --- a/static/main.js +++ b/static/main.js @@ -31819,15 +31819,6 @@ }; // frontend/main.ts - var markdown = `# Milkdown Vanilla Commonmark - -> You're scared of a world where you're needed. - -This is a demo for using Milkdown with **Vanilla Typescript**. - -- A list -- [ ] Something to do -- [x] Something done`; function configureListItem(ctx) { ctx.set(listItemBlockConfig.key, { renderLabel: (label, listType, checked) => { @@ -31842,14 +31833,20 @@ This is a demo for using Milkdown with **Vanilla Typescript**. } }); } - Oe.make().config((ctx) => { - ctx.set(ce, "#editor"); - ctx.set(re, markdown); - const listener = ctx.get(h4); - listener.markdownUpdated((ctx2, markdown2, prevMarkdown) => { - if (markdown2 !== prevMarkdown) { - console.log(markdown2); - } - }); - }).config(configureListItem).use(cr).use(wt).use(c5).use(U5).use(listItemBlockComponent).create(); + function createEditor(rootId, fieldId, content3) { + Oe.make().config((ctx) => { + ctx.set(ce, rootId); + ctx.set(re, content3); + const listener = ctx.get(h4); + listener.markdownUpdated((ctx2, markdown, prevMarkdown) => { + if (markdown !== prevMarkdown) { + console.log(markdown); + console.log(fieldId); + document.getElementById(fieldId).value = markdown; + console.log("updated"); + } + }); + }).config(configureListItem).use(cr).use(wt).use(c5).use(U5).use(listItemBlockComponent).create(); + } + window.createEditor = createEditor; })(); diff --git a/static/style.css b/static/style.css index e4653d8..081fd20 100644 --- a/static/style.css +++ b/static/style.css @@ -1166,6 +1166,23 @@ select { height: auto; } +.textarea { + min-height: 3rem; + flex-shrink: 1; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + line-height: 2; + border-radius: var(--rounded-btn, 0.5rem); + border-width: 1px; + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); +} + .btm-nav > * .label { font-size: 1rem; line-height: 1.5rem; @@ -1607,6 +1624,38 @@ select { } } +.textarea:focus { + box-shadow: none; + border-color: var(--fallback-bc,oklch(var(--bc)/0.2)); + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.textarea-disabled, + .textarea:disabled, + .textarea[disabled] { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); + --tw-bg-opacity: 1; + background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); + color: var(--fallback-bc,oklch(var(--bc)/0.4)); +} + +.textarea-disabled::-moz-placeholder, .textarea:disabled::-moz-placeholder, .textarea[disabled]::-moz-placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + +.textarea-disabled::placeholder, + .textarea:disabled::placeholder, + .textarea[disabled]::placeholder { + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-placeholder-opacity))); + --tw-placeholder-opacity: 0.2; +} + @keyframes toast-pop { 0% { transform: scale(0.9); @@ -2239,6 +2288,10 @@ select { display: flex; } +.hidden { + display: none; +} + .h-16 { height: 4rem; } @@ -2255,6 +2308,11 @@ select { height: 100%; } +.h-max { + height: -moz-max-content; + height: max-content; +} + .min-h-full { min-height: 100%; } @@ -2287,6 +2345,11 @@ select { width: 100%; } +.w-max { + width: -moz-max-content; + width: max-content; +} + .flex-1 { flex: 1 1 0%; } @@ -2404,6 +2467,16 @@ select { border-color: rgb(229 231 235 / var(--tw-border-opacity)); } +.border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0 / var(--tw-border-opacity)); +} + +.border-gray-400 { + --tw-border-opacity: 1; + border-color: rgb(156 163 175 / var(--tw-border-opacity)); +} + .bg-accent { --tw-bg-opacity: 1; background-color: var(--fallback-a,oklch(var(--a)/var(--tw-bg-opacity))); diff --git a/templates/documents/edit_document.html b/templates/documents/edit_document.html new file mode 100644 index 0000000..3a86327 --- /dev/null +++ b/templates/documents/edit_document.html @@ -0,0 +1,48 @@ + + +{% include "head.html" %} + + +
+ {% set current_page = "documents" %} + {% include "components/sidebar.html" %} + +
+
+ + +
+ + +
+ + + +
+
+
+ + + + + + diff --git a/templates/documents/list_documents.html b/templates/documents/list_documents.html index 3a05171..c801c1e 100644 --- a/templates/documents/list_documents.html +++ b/templates/documents/list_documents.html @@ -26,7 +26,7 @@ {% for document in documents %}
-

{{ document.name }}

+

{{ document.title }}