Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table updates #106

Merged
merged 28 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
521cb44
Add the ability to hide columns via keyboard commands
CannonLock Dec 19, 2023
0f2d997
Add a better default text editor
CannonLock Dec 19, 2023
2ea0b01
Use sources api v3 for edit tables
CannonLock Dec 21, 2023
ae8fb15
Update long-text cells on source update
CannonLock Dec 21, 2023
4769c5d
Fix function copies using destructing instead
CannonLock Dec 22, 2023
5ae38c8
Update when no data
CannonLock Dec 22, 2023
d12eaa7
Fix bug that transformed data to dict type post update application
CannonLock Jan 3, 2024
d7d62aa
Fix bug due to improper dataParameter cloning
CannonLock Jan 3, 2024
33df9f5
Reset filters and groups on submission
CannonLock Jan 4, 2024
c1770ba
Edit pages behind auth
CannonLock Jan 12, 2024
08477cd
Put the edit pages behind auth and slightly freshen the map page
CannonLock Jan 12, 2024
319b2f5
Fix how the raster tag renders
CannonLock Jan 12, 2024
7fcb85c
Pass the ingest_api
CannonLock Jan 12, 2024
b092fa8
Update package.json commands
CannonLock Jan 12, 2024
8d04a1c
Add back source_id to card
CannonLock Jan 16, 2024
283614d
Make Columns that have applied filters more visible
CannonLock Jan 16, 2024
e204e2f
Table Improvements
CannonLock Jan 19, 2024
011292c
Table Improvements
CannonLock Jan 19, 2024
d881258
Prevent the menu from closing on select
CannonLock Jan 19, 2024
66ad858
Only show active filter if it is valid
CannonLock Jan 19, 2024
a5a7a37
Fix bug where data remained if filter removed everything
CannonLock Jan 19, 2024
59ea06a
Fix warning resulting from controlling select with boolean 'selected'
CannonLock Jan 19, 2024
ba6a1ea
Repopulate data only once on submit
CannonLock Jan 19, 2024
681ead8
Merge in upstream/main
CannonLock Jan 22, 2024
17ccfc1
Update yarn release from merge
CannonLock Jan 22, 2024
65d7f09
Update web-components
CannonLock Jan 22, 2024
a87e212
Finish Merge
CannonLock Jan 23, 2024
01028c3
Update yarn.lock
CannonLock Jan 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"bootstrap": "yarn",
"dev": "yarn run server:dev",
"build": "vite build",
"server": "node --env-file=.env ./server/index.js",
"server:dev": "yarn run server",
"server": "node ./server/index.js",
"server:dev": "node --env-file=.env ./server/index.js",
"server:prod": "NODE_ENV=production npm run server"
},
"workspaces": [
Expand Down
2 changes: 2 additions & 0 deletions packages/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const mapboxAccessToken = import.meta.env.VITE_MAPBOX_API_TOKEN;
export const apiV2Prefix =
import.meta.env.VITE_MACROSTRAT_API_V2 ?? apiDomain + "/api/v2";

export const ingestPrefix = import.meta.env.VITE_MACROSTRAT_INGEST_API;

export const mapPagePrefix = "/map";

export const routerBasename = import.meta.env.BASE_URL + "map";
Expand Down
80 changes: 73 additions & 7 deletions src/pages/maps/+Page.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import hyper from "@macrostrat/hyper";

// Styles

import "../../styles/padding.css";

// Page for a list of maps
import styles from "./main.module.sass";
import { tempImageIndex, s3Address } from "./raster-images";
import { Icon, IconSize } from "@blueprintjs/core";
import { Icon, IconSize, Navbar, AnchorButton, Tooltip, Card } from "@blueprintjs/core";

const h = hyper.styled(styles);

export function Page({ sources }) {
export function Page({ sources, user, url, ingest_api }) {

const sources1 = sources.map((source) => {
const { source_id } = source;
const image = tempImageIndex[source_id];
Expand All @@ -16,11 +22,71 @@ export function Page({ sources }) {
});

return h("div", [
h("h1", "Maps"),
h(
"ul",
sources1.map((d) => h(SourceItem, { source: d, key: d.source_id }))
),
h(Navbar, {}, [
h(Navbar.Group, { align: "left" }, [
h(Navbar.Heading, "Source Maps")
]),
h(Navbar.Group, { align: "right" }, [
h(Tooltip, { content: user == undefined ? "Log In" : "Logged In", }, h(AnchorButton, {
icon: user == undefined ? "log-in" : "user",
style: {
margin: "0 0.5em",
borderRadius: "50%",
backgroundColor: user == undefined ? "#fdeb88" : "#90d090",
},
href: `${ingest_api}/security/login?return_url=${url}`,
})),
])
]),
h("div", {
style: {
display: "flex",
justifyContent: "center",
flexDirection: "column"
}
}, [
...sources1.map((d) => {
return h("div", {style: {maxWidth: "1000px", minWidth: "50%", margin: "auto"}}, [h(SourceCard, { source: d, key: d.source_id, user: user })])
})
])
]);
}

interface Source {
source_id: number;
name: string;
scale: number;
rasterURL?: string;
}

const SourceCard = ({source, user}: {source: Source, user: any | undefined}) => {

const href = `/maps/${source.source_id}`;
const edit_href = `/maps/${source.source_id}/edit`;

return h(Card, {
style: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
padding: "0.5em",
margin: "0.5em",
borderRadius: "0.5em",
backgroundColor: "#f0f0f0"
}
}, [
h("div", {}, [
h("h4", {style: {margin: "0px"}}, source.source_id + " " + source.name),
h("h6", {style: {margin: "0px"}}, source.scale),
h.if(source.rasterURL != null)([" ", h("span.raster", {style: {marginTop: ".5rem"}}, "Raster")]),
]),
h("div", {}, [
h(AnchorButton, { href: href, icon: "map" }, "View"),
h.if(user !== undefined)([
"",
h(AnchorButton, { href: edit_href, icon: "edit" }, "Edit")
])
]),
]);
}

Expand Down
24 changes: 17 additions & 7 deletions src/pages/maps/+onBeforeRender.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { apiV2Prefix } from "@macrostrat-web/settings";
import { ingestPrefix } from "@macrostrat-web/settings";
import fetch from "node-fetch";

const apiAddress = apiV2Prefix + "/defs/sources";
const apiAddress = ingestPrefix + "/sources";

export async function onBeforeRender(pageContext) {
// `.page.server.js` files always run in Node.js; we could use SQL/ORM queries here.
const response = await fetch(apiAddress + "?format=json");
const res = await response.json();
let sources = res.success.data;
const url = new URL(apiAddress);
url.searchParams.set("page_size", "9999");



const response = await fetch(url.toString());
const sources = await response.json();

sources.sort((a, b) => a.source_id - b.source_id);

const pageProps = { sources };
const pageProps = {
sources: sources,
user: pageContext.user,
url: pageContext.url,
ingest_api: ingestPrefix
};
return {
pageContext: {
pageProps,
pageProps
},
};
}
28 changes: 0 additions & 28 deletions src/pages/maps/@id/edit/+Page.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,6 @@
import { apiV2Prefix } from "@macrostrat-web/settings";
import h from "@macrostrat/hyper";
import { PageContextBuiltInServer } from "vike/types";
import { ClientOnly } from "~/renderer/client-only";

const apiAddress = apiV2Prefix + "/defs/sources";

export async function onBeforeRender(pageContext: PageContextBuiltInServer) {
const { id } = pageContext.routeParams;

const params = new URLSearchParams({
format: "geojson",
source_id: id,
});
const response = await fetch(apiAddress + "?" + params);
const data: any = await response.json();
const map = data?.success?.data?.features[0];

return {
pageContext: {
pageProps: {
id,
map,
},
documentProps: {
// The page's <title>
title: map.properties.name,
},
},
};
}

const EditInterface = () => import("./edit-page");

Expand Down
29 changes: 29 additions & 0 deletions src/pages/maps/@id/edit/+onBeforeRender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { apiV2Prefix } from "@macrostrat-web/settings";
import { PageContextBuiltInServer } from "vike/types";

const apiAddress = apiV2Prefix + "/defs/sources/";

export async function onBeforeRender(pageContext: PageContextBuiltInServer) {
const { id } = pageContext.routeParams;

const params = new URLSearchParams({
format: "geojson",
source_id: id,
});
const response = await fetch(apiAddress + "?" + params);
const data: any = await response.json();
const map = data?.success?.data?.features[0];

return {
pageContext: {
pageProps: {
id,
map,
},
documentProps: {
// The page's <title>
title: map.properties.name,
},
},
};
}
20 changes: 20 additions & 0 deletions src/pages/maps/@id/edit/+route.ts
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
export default "/maps/@id/edit/*";

import { render, redirect } from 'vike/abort'

export const guard = (pageContext) => {
const { user } = pageContext

if (user === undefined) {
// Render the login page while preserving the URL. (This is novel technique
// which we explain down below.)
throw redirect(`${import.meta.env.VITE_MACROSTRAT_INGEST_API}/security/login?return_url=${pageContext.url}`)
/* The more traditional way, redirect the user:
throw redirect('/login')
*/
}
if (!user.groups.includes(1)) {
// Render the error page and show message to the user
return render(403, 'Only admins are allowed to access this page.')
}
}

54 changes: 49 additions & 5 deletions src/pages/maps/@id/edit/components/cell/editable-cell.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import React, { forwardRef, useEffect } from "react";

import {EditableCell2, EditableCell2Props} from "@blueprintjs/table";
import {Cell, EditableCell2Props} from "@blueprintjs/table";

import hyper from "@macrostrat/hyper";
import styles from "./main.module.sass";
Expand All @@ -14,7 +14,51 @@ interface EditableCell extends EditableCell2Props {
rowIndex: number
}

export const EditableCell = ({...props}: EditableCell2Props) => {
export const EditableCell = forwardRef((props: EditableCell2Props, ref) => {

return h(EditableCell2, {...props});
}
const [value, setValue] = React.useState(props.value);

useEffect(() => {
setValue(props.value)
}, [props.value])

return h(Cell,
{...props,
style: {...props.style, padding: 0},
truncated: false
},
[
h("input", {
ref: ref,
className: "editable-cell",
style: {
width: (value?.length ?? 2) + "ch"
},
value: value || "",
onChange: (e) => {
setValue(e.target.value)
e.target.style.width = (e.target.value.length + 8) + "ch"
console.log(e.target.value.length)
},
onClick: (e) => {
e.target.select()
},
onFocus: (e) => {
console.log("Focus")
e.target.select()
e.target.select()
e.target.select()
e.target.select()
e.preventDefault()
e.stopPropagation()

},
onBlur: (e) => {
if(value != props.value) {
props.onConfirm(value)
}
}
}, [])
]
)
})
79 changes: 79 additions & 0 deletions src/pages/maps/@id/edit/components/cell/long-text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {Button, MenuItem} from "@blueprintjs/core";
import { Popover2 } from "@blueprintjs/popover2";
import { Select2, ItemRenderer, ItemPredicate } from "@blueprintjs/select";
import {EditableCell2Props, EditableCell2, Cell} from "@blueprintjs/table";
import React, {useEffect, useMemo, forwardRef} from "react";

// @ts-ignore
import hyper from "@macrostrat/hyper";

import "@blueprintjs/core/lib/css/blueprint.css"
import "@blueprintjs/popover2/lib/css/blueprint-popover2.css";
import styles from "../../edit-table.module.sass";


const h = hyper.styled(styles);

const LongTextCell = forwardRef((props: EditableCell2Props, forwardRef) => {

const {value, onConfirm} = props;

const [localValue, setLocalValue] = React.useState(value == null ? "" : value.toString());

useEffect(() => {
setLocalValue(value == null ? "" : value.toString())
}, [value])

return h(Cell, {
...props,
style: {...props.style, padding: 0},
}, [
h(Popover2,
{
interactionKind: "click",
placement: "bottom-start",
minimal: true,
content: h("div", {style: {padding: ""}}, [
h("textarea", {
autoFocus: true,
onFocus: (e) => {
e.target.select()
},
rows: (localValue.match(/\n/g) || []).length + 2,
style: {width: "100%", height: "100%", border: "0", padding: "5px", marginBottom: "-5px"},
value: localValue,
onWheelCapture: (event) => event.stopPropagation(),
onKeyDown: (e) => {
if(e.key == "Enter"){
setLocalValue(localValue + "\n")
}
},
onChange: (e) => {
setLocalValue(e.target.value)
e.preventDefault()
}
})
]),
onClose: () => {
onConfirm(localValue)
},
renderTarget: ({isOpen, ref, ...targetProps }) => h(Button, {
...targetProps,
onDoubleClick: (e) => {targetProps.onClick(e); e.stopPropagation()},
onClick: (e) => {e.stopPropagation()},
elementRef: (el) => {ref(el); forwardRef(el);},
style: {backgroundColor: "white", fontSize: "12px", minHeight: "0px", padding: "1.7px 10px", boxShadow: "none"},
fill: true,
alignText: "left",
text: h("span", {style: {overflow: "hidden", textOverflow: "ellipses"}}, localValue),
className: "update-input-group",
placeholder: "Select A Filter"
}, [])
}

)
])
})


export default LongTextCell;
Loading