From 2dec1335a7576706baf47553e349e2679c990bcd Mon Sep 17 00:00:00 2001 From: Daniele Guido Date: Fri, 15 Mar 2024 11:17:59 +0100 Subject: [PATCH] add login --- .env | 3 +- Makefile | 2 + gatsby-browser.js | 56 +++++++++--------- gatsby-config.js | 20 ++++++- package.json | 3 + src/components/Header.js | 4 +- src/components/ModalLogin.js | 100 +++++++++++++++++++++++++-------- src/components/Modals.js | 12 ++-- src/components/NotebookCard.js | 13 ++--- src/components/PageLayout.js | 2 - src/components/TutorialCard.js | 2 +- src/components/UserArea.js | 34 +++++++++++ src/components/Wall.js | 5 +- src/styles/style.css | 17 +++++- yarn.lock | 44 ++++++++++++++- 15 files changed, 236 insertions(+), 81 deletions(-) create mode 100644 src/components/UserArea.js diff --git a/.env b/.env index 2810d83..a09eacb 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -GATSBY_PATH_PREFIX=/impresso-datalab \ No newline at end of file +GATSBY_PATH_PREFIX=/impresso-datalab +GATSBY_IMPRESSO_API_URL=http://localhost:8000/api \ No newline at end of file diff --git a/Makefile b/Makefile index 96f54c9..f761fc8 100644 --- a/Makefile +++ b/Makefile @@ -8,4 +8,6 @@ run-dev: GIT_REPO=$(shell git config --get remote.origin.url) \ PATH_PREFIX=/datalab \ PREFIX_PATHS=true \ + GATSBY_PATH_PREFIX="" \ + GATSBY_IMPRESSO_API_URL="https://impresso-project.ch/api" \ yarn start \ No newline at end of file diff --git a/gatsby-browser.js b/gatsby-browser.js index 358d750..2da7eb1 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -4,7 +4,7 @@ import "./src/styles/fonts.css" import "bootstrap/dist/css/bootstrap.min.css" import "./src/styles/style.css" import Modals from "./src/components/Modals" -import { AvailableModalsViews, useBrowserStore } from "./src/store" +// import { AvailableModalsViews } from "./src/store" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import PrefetchData from "./src/components/PrefetchData" import PageLayout from "./src/components/PageLayout" @@ -13,35 +13,35 @@ import PageLayout from "./src/components/PageLayout" export function onRouteUpdate({ location, prevLocation }) { console.log("[Layout] render") console.log("[gatsby-browser]@onRouteUpdate new pathname", location) - let view = null - let viewId = null - // update zustand with search params, if any - if (location.search) { - const params = new URLSearchParams(location.search) - const paramView = params.get("view") - const paramViewId = params.get("viewId") + // let view = null + // let viewId = null + // // update zustand with search params, if any + // if (location.search) { + // const params = new URLSearchParams(location.search) + // const paramView = params.get("view") + // const paramViewId = params.get("viewId") - if (AvailableModalsViews.includes(paramView)) { - console.log("[gatsby-browser]@onRouteUpdate view:", paramView) - view = String(paramView) - } else { - console.log( - "[gatsby-browser]@onRouteUpdate view not recognized, close everything:", - paramView - ) - } + // if (AvailableModalsViews.includes(paramView)) { + // console.log("[gatsby-browser]@onRouteUpdate view:", paramView) + // view = String(paramView) + // } else { + // console.log( + // "[gatsby-browser]@onRouteUpdate view not recognized, close everything:", + // paramView + // ) + // } - // test view id against a regex (lowercase letters and numbers, only trailing slash) - if (paramViewId && /^[a-z0-9-]+$/.test(paramViewId)) { - console.log("[gatsby-browser]@onRouteUpdate viewId:", paramViewId) - viewId = String(paramViewId) - } else { - console.log( - "[gatsby-browser]@onRouteUpdate viewId not recognized, close everything:", - paramViewId - ) - } - } + // // test view id against a regex (lowercase letters and numbers, only trailing slash) + // if (paramViewId && /^[a-z0-9-]+$/.test(paramViewId)) { + // console.log("[gatsby-browser]@onRouteUpdate viewId:", paramViewId) + // viewId = String(paramViewId) + // } else { + // console.log( + // "[gatsby-browser]@onRouteUpdate viewId not recognized, close everything:", + // paramViewId + // ) + // } + // } // always update store with view and viewId // useBrowserStore.setState({ diff --git a/gatsby-config.js b/gatsby-config.js index ea03fed..0f06944 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -1,10 +1,14 @@ /** * @type {import('gatsby').GatsbyConfig} */ - +const { createProxyMiddleware } = require("http-proxy-middleware") require("dotenv").config({ path: [`.env.${process.env.NODE_ENV}`, ".env"], }) +const ApiHost = ( + process.env.GATSBY_IMPRESSO_API_URL || "http://localhost:8000/api" +).replace("/api", "") + console.log("gatsby-config") console.log("NODE_ENV", process.env.NODE_ENV) console.log("PATH_PREFIX", process.env.PATH_PREFIX) @@ -13,6 +17,8 @@ console.log("GIT_BUILD_TAG", process.env.GIT_BUILD_TAG) console.log("GIT_BRANCH", process.env.GIT_BRANCH) console.log("GIT_REVISION", process.env.GIT_REVISION) console.log("GIT_REPO", process.env.GIT_REPO) +console.log("GATSBY_IMPRESSO_API_URL", process.env.GATSBY_IMPRESSO_API_URL) +console.log("ApiHost", ApiHost) module.exports = { siteMetadata: { @@ -24,6 +30,18 @@ module.exports = { gitRepo: process.env.GIT_REPO, }, pathPrefix: process.env.PATH_PREFIX || "/", + developMiddleware: (app) => { + app.use( + "/api", + createProxyMiddleware({ + target: ApiHost, + changeOrigin: true, + pathRewrite: { + "^/api": "/api", + }, + }) + ) + }, plugins: [ "gatsby-plugin-sitemap", // { diff --git a/package.json b/package.json index 3bb236a..61eaea9 100644 --- a/package.json +++ b/package.json @@ -40,5 +40,8 @@ "react-bootstrap": "^2.10.0", "react-dom": "^18.2.0", "zustand": "^4.5.2" + }, + "devDependencies": { + "http-proxy-middleware": "^2.0.6" } } diff --git a/src/components/Header.js b/src/components/Header.js index 775e0cb..fbbb1ff 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -10,6 +10,7 @@ import { import { Link } from "gatsby" import "./Header.css" import LogoImpressoDataLab from "./_svg/LogoImpressoDataLab" +import UserArea from "./UserArea" const Header = () => { return ( @@ -37,11 +38,12 @@ const Header = () => { placeholder="search notebooks..." aria-label="Search Notebooks" /> - + diff --git a/src/components/ModalLogin.js b/src/components/ModalLogin.js index c758050..5a0d06d 100644 --- a/src/components/ModalLogin.js +++ b/src/components/ModalLogin.js @@ -1,34 +1,86 @@ -import React from 'react' -import ModalView from './ModalView' -import { ModalLoginView } from '../store' -import { Button, Form, Modal } from 'react-bootstrap' +import React, { useRef } from "react" +import ModalView from "./ModalView" +import { ModalLoginView } from "../store" +import { Button, Form, Modal } from "react-bootstrap" +import { useMutation } from "@tanstack/react-query" +import axios from "axios" const ModalLogin = ({ onClose, ...props }) => { + const formPayload = useRef({ + email: "", + password: "", + strategy: "local", + }) + + const { mutate, status, error, data } = useMutation({ + mutationFn: (data) => { + console.info("[ModalLogin] mutate", data.email) + return axios + .post("/api/authentication", data) + .then((res) => { + console.info("[ModalLogin] mutate success", res.data) + formPayload.current = { + email: "", + password: "", + strategy: "local", + } + return res.data + }) + .catch((err) => { + console.error( + "[ModalLogin] mutate error", + err.code, + err.response?.data + ) + + return err + }) + }, + }) + + const onSubmitHandler = (e) => { + e.preventDefault() + console.info("[ModalLogin] onSubmitHandler", formPayload.current) + mutate(formPayload.current) + } + // # get ac return ( - - - - Using Grid in Modal - - - -
- + + + + Login + + + Email address - + (formPayload.current.email = e.target.value)} + type="email" + placeholder="name@example.com" + /> - + Password - + (formPayload.current.password = e.target.value)} + type="password" + /> - - - - - - +
+ + + + +
) } diff --git a/src/components/Modals.js b/src/components/Modals.js index d5bce63..71f2e1c 100644 --- a/src/components/Modals.js +++ b/src/components/Modals.js @@ -1,25 +1,21 @@ import React from "react" -import { AvailableModalsViews } from "../store" +import { AvailableModalsViews, useBrowserStore } from "../store" import { Button } from "react-bootstrap" -import { navigate } from "gatsby" import ModalLogin from "./ModalLogin" import ModalNotebookPreview from "./ModalNotebookPreview" import ModalTutorial from "./ModalTutorial" const Modals = ({ debug = false }) => { + const setView = useBrowserStore((state) => state.setView) const onCloseHandler = () => { - navigate("") + setView(null) } return ( <> {debug && AvailableModalsViews.map((modal) => ( - ))} diff --git a/src/components/NotebookCard.js b/src/components/NotebookCard.js index 27b5756..e6adfaf 100644 --- a/src/components/NotebookCard.js +++ b/src/components/NotebookCard.js @@ -1,9 +1,8 @@ import React from "react" -import { ModalNotebookPreviewView, useDataStore } from "../store" +import { useDataStore } from "../store" import AuthorCard from "./AuthorCard" -import { Button } from "react-bootstrap" import "./NotebookCard.css" -import { Link, navigate } from "gatsby" +import { Link } from "gatsby" import Avatar from "boring-avatars" import { ArrowRight } from "iconoir-react" import { DateTime } from "luxon" @@ -12,9 +11,7 @@ import { DateTime } from "luxon" const NotebookCard = ({ name }) => { const getNotebookByName = useDataStore((state) => state.getNotebookByName) const notebook = getNotebookByName(name) - const navigateToNotebookPage = () => { - navigate(`?view=${ModalNotebookPreviewView}&viewId=${name}`) - } + const accessTime = notebook?.accessTime const accessDateTime = DateTime.fromISO(accessTime) @@ -33,9 +30,7 @@ const NotebookCard = ({ name }) => {
{accessDateTime.toFormat("yyyy LLL dd")}
-

- {notebook?.title} -

+

{notebook?.title}

    {notebook?.authors.map((name) => (
  1. diff --git a/src/components/PageLayout.js b/src/components/PageLayout.js index 2ae4a79..e0cbe64 100644 --- a/src/components/PageLayout.js +++ b/src/components/PageLayout.js @@ -1,8 +1,6 @@ -import { useSpring } from "@react-spring/web" import { Link } from "gatsby" import React, { useEffect, useRef, useState } from "react" import { Button, Modal } from "react-bootstrap" -import ModalNotebookPreview from "./ModalNotebookPreview" const PageLayout = ({ children, path, pageContext }) => { console.log("[PageLayout] render props") diff --git a/src/components/TutorialCard.js b/src/components/TutorialCard.js index 8264fd1..7f2ec98 100644 --- a/src/components/TutorialCard.js +++ b/src/components/TutorialCard.js @@ -1,7 +1,7 @@ import React from "react" import "./TutorialCard.css" import { useDataStore } from "../store" -import { Link, navigate } from "gatsby" +import { Link } from "gatsby" const TutorialCard = ({ name }) => { const [, getTutorialByName] = useDataStore((state) => [ diff --git a/src/components/UserArea.js b/src/components/UserArea.js new file mode 100644 index 0000000..ccc09a5 --- /dev/null +++ b/src/components/UserArea.js @@ -0,0 +1,34 @@ +import { useQuery } from "@tanstack/react-query" +import axios from "axios" +import React from "react" +import { Button } from "react-bootstrap" +import { ModalLoginView, useBrowserStore } from "../store" + +const UserArea = () => { + const setView = useBrowserStore((state) => state.setView) + const { status, data, error } = useQuery({ + queryKey: ["me"], + queryFn: () => axios.get(`/api/me`), + retry: false, + refetchOnMount: false, + refetchOnReconnect: false, + }) + + console.log("[UserArea]", status, data, error) + return ( +
    + + +
    + ) +} + +export default UserArea diff --git a/src/components/Wall.js b/src/components/Wall.js index e06fbf6..cdba87b 100644 --- a/src/components/Wall.js +++ b/src/components/Wall.js @@ -88,6 +88,7 @@ const Wall = () => { ) : (

    No highlighted collections found.

    )} +

    Give your media monitoring a boost.

    @@ -99,9 +100,7 @@ const Wall = () => { {collections.totalCount} collections of notebooks, developed by {authors.totalCount} authors.

    -
    - -
    +

    Quick links

    diff --git a/src/styles/style.css b/src/styles/style.css index c5329b4..9c0abbe 100644 --- a/src/styles/style.css +++ b/src/styles/style.css @@ -2,13 +2,16 @@ :root { --impresso-color-paper: #fafbf2; --impresso-color-yellow-code: 255, 235, 120; - --impresso-color-yellow: rgb(--impresso-color-yellow-code); + --impresso-color-yellow: #ffeb78; --impresso-color-black: #343a40; --impresso-color-black-rgb: 52, 58, 64; --impresso-border-radius-xs: 5px; --impresso-border-radius-sm: 20px; --impresso-border-radius-lg: 30px; --impresso-border-radius-xl: 40px; + + /* Bootstrap overrides */ + --bs-border-radius-lg: var(--impresso-border-radius-lg); --bs-font-sans-serif: "Satoshi-Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; @@ -136,6 +139,18 @@ b { .modal-backdrop { --bs-backdrop-bg: var(--impresso-color-paper); } +.modal { + --bs-modal-border-color: var(--impresso-color-black); + --bs-modal-border-width: 0px; + --bs-modal-border-radius: var(--impresso-border-radius-sm); + --bs-modal-footer-border-width: 0px; + --bs-modal-header-border-width: 0px; +} + +.modal-content { + box-shadow: var(--shadow-lg); +} + /* ... */ h2 { /* border-bottom: 1px solid var(--impresso-color-black); */ diff --git a/yarn.lock b/yarn.lock index 371db22..2981fe6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2447,7 +2447,7 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== -"@types/http-proxy@^1.17.11": +"@types/http-proxy@^1.17.11", "@types/http-proxy@^1.17.8": version "1.17.14" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== @@ -5204,6 +5204,11 @@ event-source-polyfill@1.0.31: resolved "https://registry.yarnpkg.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz#45fb0a6fc1375b2ba597361ba4287ffec5bf2e0c" integrity sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA== +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -5498,6 +5503,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +follow-redirects@^1.0.0: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + follow-redirects@^1.14.0, follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" @@ -6536,6 +6546,26 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-middleware@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -6905,6 +6935,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" @@ -8068,7 +8103,7 @@ micromark@^3.0.0: micromark-util-types "^1.0.1" uvu "^0.5.0" -micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -9640,6 +9675,11 @@ require-package-name@^2.0.1: resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"