search bar
This commit is contained in:
parent
d0d64cf018
commit
e3c477181d
13 changed files with 2234 additions and 55 deletions
|
@ -9,11 +9,20 @@
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.11.4",
|
||||||
|
"@emotion/styled": "^11.11.5",
|
||||||
|
"@floating-ui/react": "^0.26.16",
|
||||||
"@fontsource/inter": "^5.0.18",
|
"@fontsource/inter": "^5.0.18",
|
||||||
|
"@mui/icons-material": "^5.15.18",
|
||||||
|
"@mui/material": "^5.15.18",
|
||||||
"@tanstack/react-query": "^5.37.1",
|
"@tanstack/react-query": "^5.37.1",
|
||||||
"@tauri-apps/api": "^1",
|
"@tauri-apps/api": "^1",
|
||||||
|
"@uiw/react-md-editor": "^4.0.4",
|
||||||
|
"javascript-time-ago": "^2.5.10",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0",
|
||||||
|
"react-markdown": "^9.0.1",
|
||||||
|
"react-time-ago": "^7.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1",
|
"@tauri-apps/cli": "^1",
|
||||||
|
|
|
@ -6,9 +6,13 @@ import "./global.scss";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import NodeDisplay from "./components/NodeDisplay";
|
import NodeDisplay from "./components/NodeDisplay";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import TimeAgo from "javascript-time-ago";
|
||||||
|
import en from "javascript-time-ago/locale/en";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
TimeAgo.addDefaultLocale(en);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [nodesOpened, setNodesOpened] = useState<string[]>(() => []);
|
const [nodesOpened, setNodesOpened] = useState<string[]>(() => []);
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import styles from "./Header.module.scss";
|
import styles from "./Header.module.scss";
|
||||||
|
import SearchBar from "./SearchBar";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.Header}>
|
<div className={styles.Header}>
|
||||||
<span>Panorama</span>
|
<span>Panorama</span>
|
||||||
<input type="text" placeholder="Search..." />
|
<SearchBar />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,37 @@
|
||||||
.container {
|
.container {
|
||||||
width: 400px;
|
width: 500px;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdContent {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdEditor {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 2px 12px;
|
padding: 2px 12px;
|
||||||
background-color: lightgray;
|
color: rgb(106, 103, 160);
|
||||||
color: gray;
|
background: rgb(204, 201, 255);
|
||||||
|
background: linear-gradient(90deg, rgba(204, 201, 255, 1) 0%, rgba(255, 255, 255, 1) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import styles from "./NodeDisplay.module.scss";
|
import styles from "./NodeDisplay.module.scss";
|
||||||
|
import ReactTimeAgo from "react-time-ago";
|
||||||
|
import Markdown from "react-markdown";
|
||||||
|
import MDEditor, { commands } from "@uiw/react-md-editor";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export interface NodeDisplayProps {
|
export interface NodeDisplayProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -19,7 +23,11 @@ export default function NodeDisplay({ id }: NodeDisplayProps) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<small>ID {id}</small>
|
{isSuccess ? (
|
||||||
|
<NodeDisplayHeaderLoaded id={id} data={data} />
|
||||||
|
) : (
|
||||||
|
<small>ID {id}</small>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
{isSuccess ? (
|
{isSuccess ? (
|
||||||
|
@ -32,11 +40,51 @@ export default function NodeDisplay({ id }: NodeDisplayProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NodeDisplayHeaderLoaded({ id, data }) {
|
||||||
|
return (
|
||||||
|
<small>
|
||||||
|
Type {data.type} · Last updated{" "}
|
||||||
|
<ReactTimeAgo date={data.created_at * 1000} /> · {id}
|
||||||
|
</small>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function NodeDisplayLoaded({ id, data }) {
|
function NodeDisplayLoaded({ id, data }) {
|
||||||
|
const [value, setValue] = useState(() => data.content);
|
||||||
|
const [isEditing, setIsEditing] = useState(() => false);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
Node {id}
|
<details>
|
||||||
<p>{JSON.stringify(data)}</p>
|
<summary>JSON</summary>
|
||||||
|
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<button type="button" onClick={() => setIsEditing((prev) => !prev)}>
|
||||||
|
{isEditing ? "done" : "edit"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className={styles.mdContent} data-color-mode="light">
|
||||||
|
{
|
||||||
|
isEditing ? (
|
||||||
|
<>
|
||||||
|
<MDEditor
|
||||||
|
data-color-mode="light"
|
||||||
|
className={styles.mdEditor}
|
||||||
|
value={value}
|
||||||
|
onChange={setValue}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<MDEditor.Markdown
|
||||||
|
source={value}
|
||||||
|
style={{ whiteSpace: "pre-wrap" }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
// <Markdown>{data.content}</Markdown>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
9
app/src/components/SearchBar.module.scss
Normal file
9
app/src/components/SearchBar.module.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.menu {
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
// box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.25);
|
||||||
|
|
||||||
|
min-width: 500px;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
66
app/src/components/SearchBar.tsx
Normal file
66
app/src/components/SearchBar.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import styles from "./SearchBar.module.scss";
|
||||||
|
import {
|
||||||
|
FloatingFocusManager,
|
||||||
|
FloatingOverlay,
|
||||||
|
FloatingPortal,
|
||||||
|
autoUpdate,
|
||||||
|
offset,
|
||||||
|
useClick,
|
||||||
|
useDismiss,
|
||||||
|
useFloating,
|
||||||
|
useFocus,
|
||||||
|
useInteractions,
|
||||||
|
} from "@floating-ui/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export default function SearchBar() {
|
||||||
|
const [showMenu, setShowMenu] = useState(() => false);
|
||||||
|
|
||||||
|
const { refs, context, floatingStyles } = useFloating({
|
||||||
|
placement: "bottom-start",
|
||||||
|
open: showMenu,
|
||||||
|
onOpenChange: setShowMenu,
|
||||||
|
whileElementsMounted: autoUpdate,
|
||||||
|
middleware: [offset(10)],
|
||||||
|
});
|
||||||
|
const focus = useFocus(context);
|
||||||
|
const { getReferenceProps, getFloatingProps } = useInteractions([
|
||||||
|
focus,
|
||||||
|
useDismiss(context),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search..."
|
||||||
|
onFocus={() => setShowMenu(true)}
|
||||||
|
ref={refs.setReference}
|
||||||
|
{...getReferenceProps()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showMenu && (
|
||||||
|
<FloatingPortal>
|
||||||
|
<FloatingOverlay>
|
||||||
|
{/* <FloatingFocusManager context={context} modal={false}> */}
|
||||||
|
<div
|
||||||
|
ref={refs.setFloating}
|
||||||
|
className={styles.menu}
|
||||||
|
style={{ ...floatingStyles }}
|
||||||
|
{...getFloatingProps()}
|
||||||
|
>
|
||||||
|
<SearchMenu />
|
||||||
|
</div>
|
||||||
|
{/* </FloatingFocusManager> */}
|
||||||
|
</FloatingOverlay>
|
||||||
|
</FloatingPortal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchMenu({}) {
|
||||||
|
return <>Search</>;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ use axum::{
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use miette::{IntoDiagnostic, Report};
|
|
||||||
|
|
||||||
pub type AppResult<T, E = AppError> = std::result::Result<T, E>;
|
pub type AppResult<T, E = AppError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use axum::{extract::State, Json};
|
use axum::{extract::State, Json};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use cozo::{DataValue, ScriptMutability};
|
use cozo::ScriptMutability;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{ensure_ok, error::AppResult, AppState};
|
use crate::{error::AppResult, AppState};
|
||||||
|
|
||||||
pub async fn get_todays_journal_id(
|
pub async fn get_todays_journal_id(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
|
@ -29,15 +29,15 @@ pub async fn get_todays_journal_id(
|
||||||
let uuid = Uuid::now_v7();
|
let uuid = Uuid::now_v7();
|
||||||
let node_id = uuid.to_string();
|
let node_id = uuid.to_string();
|
||||||
|
|
||||||
let result = state.db.run_script_fold_err(
|
let _result = state.db.run_script_fold_err(
|
||||||
"
|
"
|
||||||
{
|
{
|
||||||
?[id] <- [[$node_id]]
|
?[id, type] <- [[$node_id, 'panorama/journal/page']]
|
||||||
:put node { id }
|
:put node { id, type }
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
?[node_id, plaintext] <- [[$node_id, '']]
|
?[node_id, content] <- [[$node_id, 'Default **content**']]
|
||||||
:put journal { node_id => plaintext }
|
:put journal { node_id => content }
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
?[day, node_id] <- [[$day, $node_id]]
|
?[day, node_id] <- [[$day, $node_id]]
|
||||||
|
@ -45,11 +45,15 @@ pub async fn get_todays_journal_id(
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
btmap! {
|
btmap! {
|
||||||
"node_id".to_owned() => node_id.into(),
|
"node_id".to_owned() => node_id.clone().into(),
|
||||||
"day".to_owned() => today.clone().into(),
|
"day".to_owned() => today.clone().into(),
|
||||||
},
|
},
|
||||||
ScriptMutability::Mutable,
|
ScriptMutability::Mutable,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return Ok(Json(json!({
|
||||||
|
"node_id": node_id
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
let node_id = result.rows[0][0].get_str().unwrap();
|
let node_id = result.rows[0][0].get_str().unwrap();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate anyhow;
|
extern crate anyhow;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate sugars;
|
extern crate sugars;
|
||||||
|
@ -13,7 +15,11 @@ mod node;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use axum::{http::Method, routing::get, Router};
|
use axum::{
|
||||||
|
http::Method,
|
||||||
|
routing::{get, post},
|
||||||
|
Router,
|
||||||
|
};
|
||||||
use cozo::DbInstance;
|
use cozo::DbInstance;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
@ -21,7 +27,9 @@ use tower::ServiceBuilder;
|
||||||
use tower_http::cors::{self, CorsLayer};
|
use tower_http::cors::{self, CorsLayer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
journal::get_todays_journal_id, migrations::run_migrations, node::get_node,
|
journal::get_todays_journal_id,
|
||||||
|
migrations::run_migrations,
|
||||||
|
node::{get_node, update_node},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -57,6 +65,7 @@ async fn main() -> Result<()> {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(|| async { "Hello, World!" }))
|
.route("/", get(|| async { "Hello, World!" }))
|
||||||
.route("/node/:id", get(get_node))
|
.route("/node/:id", get(get_node))
|
||||||
|
.route("/node/:id", post(update_node))
|
||||||
.route("/journal/get_todays_journal_id", get(get_todays_journal_id))
|
.route("/journal/get_todays_journal_id", get(get_todays_journal_id))
|
||||||
.layer(ServiceBuilder::new().layer(cors))
|
.layer(ServiceBuilder::new().layer(cors))
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
use cozo::{DbInstance, ScriptMutability};
|
use cozo::DbInstance;
|
||||||
use futures::Future;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::ensure_ok;
|
use crate::ensure_ok;
|
||||||
|
@ -9,11 +9,8 @@ pub async fn run_migrations(db: &DbInstance) -> Result<()> {
|
||||||
let migration_status = check_migration_status(db).await?;
|
let migration_status = check_migration_status(db).await?;
|
||||||
println!("migration status: {:?}", migration_status);
|
println!("migration status: {:?}", migration_status);
|
||||||
|
|
||||||
let migrations: Vec<Box<dyn for<'a> Fn(&'a DbInstance) -> Result<()>>> = vec![
|
let migrations: Vec<Box<dyn for<'a> Fn(&'a DbInstance) -> Result<()>>> =
|
||||||
Box::new(no_op),
|
vec![Box::new(no_op), Box::new(migration_01)];
|
||||||
Box::new(migration_01),
|
|
||||||
Box::new(migration_02),
|
|
||||||
];
|
|
||||||
|
|
||||||
if let MigrationStatus::NoMigrations = migration_status {
|
if let MigrationStatus::NoMigrations = migration_status {
|
||||||
let result = db.run_script_str(
|
let result = db.run_script_str(
|
||||||
|
@ -108,34 +105,26 @@ fn migration_01(db: &DbInstance) -> Result<()> {
|
||||||
:create node {
|
:create node {
|
||||||
id: String
|
id: String
|
||||||
=>
|
=>
|
||||||
|
type: String,
|
||||||
|
title: String? default null,
|
||||||
created_at: Float default now(),
|
created_at: Float default now(),
|
||||||
updated_at: Float default now(),
|
updated_at: Float default now(),
|
||||||
extra_data: Json default {},
|
extra_data: Json default {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Inverse mapping from keys to nodes
|
# Inverse mappings for easy querying
|
||||||
{ :create has_key { key: String => id: String } }
|
{ :create node_has_key { key: String => id: String } }
|
||||||
{ :create node_managed_by_app { node_id: String => app: String } }
|
{ :create node_managed_by_app { node_id: String => app: String } }
|
||||||
",
|
{ :create node_refers_to { node_id: String => other_node_id: String } }
|
||||||
"",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
ensure_ok(&result)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn migration_02(db: &DbInstance) -> Result<()> {
|
|
||||||
let result = db.run_script_str(
|
|
||||||
"
|
|
||||||
# Create journal type
|
# Create journal type
|
||||||
{ :create journal { node_id: String => plaintext: String } }
|
{ :create journal { node_id: String => content: String } }
|
||||||
{ :create journal_days { day: String => node_id: String } }
|
{ :create journal_days { day: String => node_id: String } }
|
||||||
{
|
{
|
||||||
::fts create journal:text_index {
|
::fts create journal:text_index {
|
||||||
extractor: plaintext,
|
extractor: content,
|
||||||
extract_filter: !is_null(plaintext),
|
extract_filter: !is_null(content),
|
||||||
tokenizer: Simple,
|
tokenizer: Simple,
|
||||||
filters: [Lowercase, Stemmer('english'), Stopwords('en')],
|
filters: [Lowercase, Stemmer('english'), Stopwords('en')],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
Json,
|
Json,
|
||||||
};
|
};
|
||||||
use cozo::ScriptMutability;
|
use cozo::ScriptMutability;
|
||||||
|
@ -10,17 +11,19 @@ use crate::{error::AppResult, AppState};
|
||||||
pub async fn get_node(
|
pub async fn get_node(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(node_id): Path<String>,
|
Path(node_id): Path<String>,
|
||||||
) -> AppResult<Json<Value>> {
|
) -> AppResult<(StatusCode, Json<Value>)> {
|
||||||
let result = state.db.run_script(
|
let result = state.db.run_script(
|
||||||
"
|
"
|
||||||
j[plaintext] := *journal{ node_id, plaintext }, node_id = $node_id
|
j[content] := *journal{ node_id, content }, node_id = $node_id
|
||||||
j[plaintext] := not *journal{ node_id }, node_id = $node_id, plaintext = null
|
j[content] := not *journal{ node_id }, node_id = $node_id, content = null
|
||||||
|
|
||||||
jd[day] := *journal_days{ node_id, day }, node_id = $node_id
|
jd[day] := *journal_days{ node_id, day }, node_id = $node_id
|
||||||
jd[day] := not *journal_days{ node_id }, node_id = $node_id, day = null
|
jd[day] := not *journal_days{ node_id }, node_id = $node_id, day = null
|
||||||
|
|
||||||
?[extra_data, plaintext, day] := *node{ id, extra_data },
|
?[
|
||||||
j[plaintext],
|
extra_data, content, day, created_at, updated_at, type
|
||||||
|
] := *node{ id, type, created_at, updated_at, extra_data },
|
||||||
|
j[content],
|
||||||
jd[day],
|
jd[day],
|
||||||
id = $node_id
|
id = $node_id
|
||||||
:limit 1
|
:limit 1
|
||||||
|
@ -29,15 +32,34 @@ pub async fn get_node(
|
||||||
ScriptMutability::Immutable,
|
ScriptMutability::Immutable,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if result.rows.len() == 0 {
|
||||||
|
return Ok((StatusCode::NOT_FOUND, Json(json!(null))));
|
||||||
|
}
|
||||||
|
|
||||||
let row = &result.rows[0];
|
let row = &result.rows[0];
|
||||||
let extra_data = row[0].get_str();
|
let extra_data = row[0].get_str();
|
||||||
let plaintext = row[1].get_str();
|
|
||||||
let day = row[2].get_str();
|
let day = row[2].get_str();
|
||||||
|
|
||||||
Ok(Json(json!({
|
Ok((
|
||||||
"node": node_id,
|
StatusCode::OK,
|
||||||
"extra_data": extra_data,
|
Json(json!({
|
||||||
"plaintext": plaintext,
|
"node": node_id,
|
||||||
"day": day,
|
"extra_data": extra_data,
|
||||||
})))
|
"content": row[1].get_str(),
|
||||||
|
"day": day,
|
||||||
|
"created_at": row[3].get_float(),
|
||||||
|
"updated_at": row[4].get_float(),
|
||||||
|
"type": row[5].get_str(),
|
||||||
|
})),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct NodeUpdate {}
|
||||||
|
|
||||||
|
pub async fn update_node(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(node_id): Path<String>,
|
||||||
|
) -> AppResult<Json<Value>> {
|
||||||
|
Ok(Json(json!({})))
|
||||||
}
|
}
|
||||||
|
|
1999
pnpm-lock.yaml
1999
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue