data is being loaded into preact SPA

This commit is contained in:
Vivian Lim 2020-07-19 18:26:49 -07:00
parent a0436f8c87
commit d7baadec6c
12 changed files with 123 additions and 67 deletions

16
Cargo.lock generated
View File

@ -211,6 +211,8 @@ dependencies = [
"libsqlite3-sys",
"rocket",
"rocket_contrib",
"serde",
"serde_json",
]
[[package]]
@ -978,6 +980,20 @@ name = "serde"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.34",
]
[[package]]
name = "serde_json"

View File

@ -13,4 +13,6 @@ anyhow = "1.0"
chrono = "0.4"
diesel = { version = "1.4.5", features = ["sqlite"] }
dotenv = "0.15.0"
libsqlite3-sys = { version = "0.18.0", features = ["bundled"] }
libsqlite3-sys = { version = "0.18.0", features = ["bundled"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ import { FunctionalComponent, h } from "preact";
import { Route, Router, RouterOnChangeArgs } from "preact-router";
import Home from "../routes/home";
import Profile from "../routes/profile";
import Dataset from "../routes/dataset";
import NotFoundPage from '../routes/notfound';
import Header from "./header";
@ -23,8 +23,7 @@ const App: FunctionalComponent = () => {
<Header />
<Router onChange={handleRoute}>
<Route path="/" component={Home} />
<Route path="/profile/" component={Profile} user="me" />
<Route path="/profile/:user" component={Profile} />
<Route path="/view/:name" component={Dataset} />
<NotFoundPage default />
</Router>
</div>

View File

@ -5,17 +5,11 @@ import * as style from "./style.css";
const Header: FunctionalComponent = () => {
return (
<header class={style.header}>
<h1>Preact App</h1>
<h1>datalog viewer thing</h1>
<nav>
<Link activeClassName={style.active} href="/">
Home
</Link>
<Link activeClassName={style.active} href="/profile">
Me
</Link>
<Link activeClassName={style.active} href="/profile/john">
John
</Link>
</nav>
</header>
);

View File

@ -0,0 +1,42 @@
import { Component, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import * as style from "./style.css";
export interface DatasetProps {
name: string,
}
export interface Datapoint {
id: number,
devicename: string,
value: number,
timestamp: string,
}
export interface DatasetState {
data: Datapoint[],
}
class Dataset extends Component<DatasetProps, DatasetState> {
constructor(){
super();
this.state = { data: [] };
}
async componentDidMount() {
var devicesResponse = await fetch(`/api/device/${this.props.name}`);
this.setState({ data: await devicesResponse.json() as Datapoint[] })
}
// Lifecycle: Called just before our component will be destroyed
componentWillUnmount() {
// stop when not renderable
}
render() {
var deviceLinks = this.state.data.map((d) => <div>{d.devicename},{d.value},{d.timestamp}</div>)
return <div class={style.profile}>{this.props.name}<span>{deviceLinks}</span></div>;
}
}
export default Dataset;

View File

@ -1,13 +1,35 @@
import { FunctionalComponent, h } from "preact";
import { Component, h } from "preact";
import { Link } from "preact-router/match";
import * as style from "./style.css";
const Home: FunctionalComponent = () => {
return (
<div class={style.home}>
<h1>Home</h1>
<p>This is the Home component.</p>
</div>
);
};
export interface HomeProps {
}
export interface HomeState {
devices: string[],
}
class Home extends Component<HomeProps, HomeState> {
constructor(){
super();
this.state = { devices: [] };
}
async componentDidMount() {
var devicesResponse = await fetch('/api/devices');
this.setState({ devices: await devicesResponse.json() as string[] })
}
// Lifecycle: Called just before our component will be destroyed
componentWillUnmount() {
// stop when not renderable
}
render() {
var deviceLinks = this.state.devices.map((d) => <div><Link href={`/view/${d}`}>{d}</Link></div>)
return <div class={style.home}><span>{deviceLinks}</span></div>;
}
}
export default Home;

View File

@ -1,44 +0,0 @@
import { FunctionalComponent, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import * as style from "./style.css";
interface Props {
user: string;
}
const Profile: FunctionalComponent<Props> = (props: Props) => {
const { user } = props;
const [time, setTime] = useState<number>(Date.now());
const [count, setCount] = useState<number>(0);
// gets called when this route is navigated to
useEffect(() => {
const timer = window.setInterval(() => setTime(Date.now()), 1000);
// gets called just before navigating away from the route
return () => {
clearInterval(timer);
};
}, []);
// update the current time
const increment = () => {
setCount(count + 1);
};
return (
<div class={style.profile}>
<h1>Profile: {user}</h1>
<p>This is the user profile for a user named {user}.</p>
<div>Current time: {new Date(time).toLocaleString()}</div>
<p>
<button onClick={increment}>Click Me</button> Clicked {count}{" "}
times.
</p>
</div>
);
};
export default Profile;

View File

@ -9,6 +9,9 @@ extern crate rocket_contrib;
#[macro_use]
extern crate anyhow;
#[macro_use]
extern crate serde;
extern crate chrono;
pub mod models;
@ -81,6 +84,27 @@ fn log_get(device_name: &RawStr, conn: DbConn) -> Result<String, anyhow::Error>
Ok(lines)
}
#[get("/devices")]
fn devices_get(conn: DbConn) -> Result<String, anyhow::Error> {
use self::schema::events::dsl::*;
let devices = events
.select(devicename)
.distinct()
.load::<String>(&*conn)?;
Ok(serde_json::to_string(&devices)?)
}
#[get("/device/<name>")]
fn device_get(name: &RawStr, conn: DbConn) -> Result<String, anyhow::Error> {
use self::schema::events::dsl::*;
let results = events
.filter(devicename.eq(name.as_str()))
.load::<Event>(&*conn)?;
Ok(serde_json::to_string(&results)?)
}
#[catch(404)]
fn not_found() -> Option<NamedFile> {
NamedFile::open("frontend/build/index.html").ok()
@ -93,7 +117,7 @@ fn main() {
"/",
rocket_contrib::serve::StaticFiles::from("frontend/build"),
)
.mount("/api", routes![log_new, log_get])
.mount("/api", routes![log_new, log_get, devices_get, device_get])
.register(catchers![not_found])
.launch();

View File

@ -1,6 +1,7 @@
use super::schema::events;
use serde::Serialize;
#[derive(Queryable, Debug)]
#[derive(Queryable, Debug, Serialize)]
pub struct Event {
pub id: i32,
pub devicename: String,