Renamed src to app and added login api action and remix-flat-routes to vite config main
This commit is contained in:
parent
4668eba03d
commit
2947c7f38d
2
remix/.gitignore
vendored
2
remix/.gitignore
vendored
@ -11,3 +11,5 @@ api/index.js
|
||||
api/index.js.map
|
||||
|
||||
pnpm-lock.yaml
|
||||
|
||||
tmp/*
|
||||
|
21
remix/app/api/utils/checkUserExists.ts
Normal file
21
remix/app/api/utils/checkUserExists.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// query mongodb for user objects with the email provided
|
||||
// if user exists, return true
|
||||
// if user does not exist, return false
|
||||
|
||||
import { createConnection } from './mongodb/connection';
|
||||
|
||||
export const checkUserExists = async ({ email }) => {
|
||||
|
||||
const client = await createConnection();
|
||||
const db = client.db('auth');
|
||||
const collection = db.collection('users');
|
||||
const user = await collection.findOne({ email });
|
||||
|
||||
if (user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
7
remix/app/api/utils/mongodb/connection.ts
Normal file
7
remix/app/api/utils/mongodb/connection.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { MongoClient } from 'mongodb';
|
||||
|
||||
export const createConnection = async () => {
|
||||
const client = new MongoClient(process.env.MONGODB_URI, {});
|
||||
await client.connect();
|
||||
return client;
|
||||
};
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { Flex, Button, FormControl, Input, Spinner, Link } from '@chakra-ui/react';
|
||||
import { useFetcher } from '@remix-run/react';
|
||||
|
||||
import { useLogin } from '~/api/v1/login/Login';
|
||||
import { useApi } from '~/hooks/useApi';
|
||||
|
||||
export const Login = (props) => {
|
||||
const [username, setUsername] = useState('');
|
||||
@ -10,24 +10,22 @@ export const Login = (props) => {
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const api = useFetcher();
|
||||
const api = useApi();
|
||||
|
||||
const { login } = useLogin();
|
||||
const login = api.v1.login;
|
||||
|
||||
const handleLogin = (e) => {
|
||||
const handleLogin = async (e) => {
|
||||
e?.preventDefault();
|
||||
|
||||
setLoading(true);
|
||||
|
||||
login(username, password)
|
||||
.then((response) => {
|
||||
console.log('nik response 123', response);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('nik error 123', error);
|
||||
setLoading(false);
|
||||
});
|
||||
const loginResp = await login({ username, password });
|
||||
|
||||
if (loginResp) {
|
||||
console.log('nik loginResp', loginResp);
|
||||
} else {
|
||||
console.error('nik no loginResp', loginResp);
|
||||
}
|
||||
|
||||
console.log('nik username', username);
|
||||
console.log('nik password', password);
|
@ -135,6 +135,12 @@ export const Nav = (props) => {
|
||||
></Icon> */}
|
||||
</Center>
|
||||
)}
|
||||
{/* TODO - Add conditional only show if loggedIn */}
|
||||
<Center transform={['', 'scaleX(-100%)']} cursor="pointer">
|
||||
<Link to="/logout">
|
||||
<Icon size="12px" name="🗝️"></Icon>
|
||||
</Link>
|
||||
</Center>
|
||||
<Center transform={['', 'scaleX(-100%)']} cursor="pointer">
|
||||
<Link to="/login">
|
||||
<Icon size="12px" name="🌈"></Icon>
|
@ -1,24 +1,24 @@
|
||||
import React from "react"
|
||||
import React from 'react';
|
||||
// import { Sticky, StickyContainer } from "react-sticky"
|
||||
import Sticky from "react-sticky-el"
|
||||
import { Box, Flex } from "@chakra-ui/react"
|
||||
import { useLocation, useMatches } from "@remix-run/react"
|
||||
import Sticky from 'react-sticky-el';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useLocation, useMatches } from '@remix-run/react';
|
||||
|
||||
import { Thingtime } from "./Thingtime"
|
||||
import { useThingtime } from "./useThingtime"
|
||||
import { Thingtime } from './Thingtime';
|
||||
import { useThingtime } from './useThingtime';
|
||||
|
||||
export const ThingtimeURL = (props) => {
|
||||
const { getThingtime } = useThingtime()
|
||||
const { getThingtime } = useThingtime();
|
||||
|
||||
const { pathname } = useLocation()
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const matches = useMatches()
|
||||
const matches = useMatches();
|
||||
const location = React.useMemo(() => {
|
||||
return matches[matches.length - 1]
|
||||
}, [matches])
|
||||
return matches[matches.length - 1];
|
||||
}, [matches]);
|
||||
|
||||
const path = React.useMemo(() => {
|
||||
console.log("ThingtimeURL location", location)
|
||||
console.log('ThingtimeURL location', location);
|
||||
|
||||
// const sanitisation = ["/things", "/edit", "/editor", "/code", "/coder"]
|
||||
|
||||
@ -31,64 +31,66 @@ export const ThingtimeURL = (props) => {
|
||||
// })
|
||||
|
||||
// strip the leading /path1/path2 path1 section from the path
|
||||
const pathPartOne = location?.pathname?.split("/")[2]
|
||||
const pathPartOne = location?.pathname?.split('/')[2];
|
||||
|
||||
const path = pathPartOne?.replace(/\//g, ".")
|
||||
const path = pathPartOne?.replace(/\//g, '.');
|
||||
|
||||
return path || "thingtime"
|
||||
}, [location])
|
||||
console.log('nik path', path);
|
||||
|
||||
return path || 'thingtime';
|
||||
}, [location]);
|
||||
|
||||
const thing = React.useMemo(() => {
|
||||
// remove /things/ from path
|
||||
|
||||
const ret = getThingtime(path)
|
||||
const ret = getThingtime(path);
|
||||
|
||||
return ret
|
||||
}, [path, getThingtime])
|
||||
return ret;
|
||||
}, [path, getThingtime]);
|
||||
|
||||
const inEditorMode = React.useMemo(() => {
|
||||
if (pathname.slice(0, 7) === "/editor") {
|
||||
return true
|
||||
if (pathname.slice(0, 7) === '/editor') {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
return false;
|
||||
}, [pathname]);
|
||||
|
||||
const inEditMode = React.useMemo(() => {
|
||||
if (pathname.slice(0, 5) === "/edit") {
|
||||
return true
|
||||
if (pathname.slice(0, 5) === '/edit') {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
return false;
|
||||
}, [pathname]);
|
||||
|
||||
const containerRef = React.useRef(null)
|
||||
const editorRef = React.useRef(null)
|
||||
const containerRef = React.useRef(null);
|
||||
const editorRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const scrollListener = () => {
|
||||
if (containerRef?.current?.getBoundingClientRect) {
|
||||
const { top } = containerRef?.current?.getBoundingClientRect()
|
||||
const { top } = containerRef?.current?.getBoundingClientRect();
|
||||
|
||||
editorRef.current.style.top = `${-top}px`
|
||||
editorRef.current.style.top = `${-top}px`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", scrollListener)
|
||||
window.addEventListener('scroll', scrollListener);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", scrollListener)
|
||||
}
|
||||
}, [])
|
||||
window.removeEventListener('scroll', scrollListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={containerRef}
|
||||
// position="sticky"
|
||||
position="relative"
|
||||
alignItems={inEditorMode ? "flex-start" : "center"}
|
||||
alignItems={inEditorMode ? 'flex-start' : 'center'}
|
||||
justifyContent="center"
|
||||
// overflow="scroll"
|
||||
// height="auto"
|
||||
flexDirection={inEditorMode ? "row" : "column"}
|
||||
flexDirection={inEditorMode ? 'row' : 'column'}
|
||||
maxWidth="100%"
|
||||
// maxHeight="100vh"
|
||||
>
|
||||
@ -109,18 +111,12 @@ export const ThingtimeURL = (props) => {
|
||||
path={path}
|
||||
thing={thing}
|
||||
render
|
||||
chakras={{ marginY: "200px" }}
|
||||
chakras={{ marginY: '200px' }}
|
||||
// width="600px"
|
||||
></Thingtime>
|
||||
</Box>
|
||||
)}
|
||||
<Thingtime
|
||||
edit={inEditMode}
|
||||
path={path}
|
||||
thing={thing}
|
||||
chakras={{ marginY: "200px" }}
|
||||
width="600px"
|
||||
></Thingtime>
|
||||
<Thingtime edit={inEditMode} path={path} thing={thing} chakras={{ marginY: '200px' }} width="600px"></Thingtime>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
@ -29,7 +29,7 @@ export const RainbowText = (props) => {
|
||||
userSelect="none"
|
||||
outline="none"
|
||||
contentEditable={props?.ce}
|
||||
spellcheck="false"
|
||||
spellCheck="false"
|
||||
>
|
||||
{props?.children}
|
||||
</Text>
|
@ -1,7 +1,7 @@
|
||||
// modify window / globalThis to support any properties
|
||||
// so there's no property does not exist on window typescript errors
|
||||
|
||||
// Path: app/src/global-types.d.ts
|
||||
// Path: app/global-types.d.ts
|
||||
declare global {
|
||||
interface Window {
|
||||
[key: string]: any;
|
28
remix/app/hooks/useApi.tsx
Normal file
28
remix/app/hooks/useApi.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { useAsyncFetcher } from './useAsyncFetcher';
|
||||
|
||||
export function useApi() {
|
||||
const asyncFetcher = useAsyncFetcher();
|
||||
|
||||
const v1 = {
|
||||
login: useCallback(
|
||||
async (args) => {
|
||||
const { username, password } = args;
|
||||
|
||||
console.log('nik submitting with username', username);
|
||||
console.log('nik submitting with password', password);
|
||||
|
||||
const ret = asyncFetcher.submit({ username, password }, { action: '/api/v1/login' });
|
||||
return ret;
|
||||
},
|
||||
[asyncFetcher]
|
||||
)
|
||||
};
|
||||
|
||||
const ret = {
|
||||
v1
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
44
remix/app/hooks/useAsyncFetcher.tsx
Normal file
44
remix/app/hooks/useAsyncFetcher.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useFetcher } from '@remix-run/react';
|
||||
|
||||
export function useAsyncFetcher() {
|
||||
let resolveRef = useRef<any>();
|
||||
let promiseRef = useRef<Promise<any>>();
|
||||
let fetcher = useFetcher();
|
||||
|
||||
if (!promiseRef.current) {
|
||||
promiseRef.current = new Promise((resolve) => {
|
||||
resolveRef.current = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
const resetResolver = useCallback(() => {
|
||||
promiseRef.current = new Promise((resolve) => {
|
||||
resolveRef.current = resolve;
|
||||
});
|
||||
}, [promiseRef, resolveRef]);
|
||||
|
||||
const [defaultOpts, setDefaultOpts] = useState({
|
||||
method: 'POST',
|
||||
encType: 'application/json'
|
||||
})
|
||||
|
||||
const submit = useCallback(
|
||||
async (data, opts) => {
|
||||
// @ts-ignore
|
||||
fetcher.submit(data, { ...defaultOpts, ...opts });
|
||||
return promiseRef.current;
|
||||
},
|
||||
[fetcher, promiseRef]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetcher.data && fetcher.state === 'idle') {
|
||||
resolveRef.current(fetcher.data);
|
||||
resetResolver();
|
||||
}
|
||||
}, [fetcher, resetResolver]);
|
||||
|
||||
|
||||
return { ...fetcher, submit };
|
||||
}
|
@ -13,7 +13,7 @@ export const action = async ({ request }) => {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: {
|
||||
message: 'Hello, World! $ Action'
|
||||
message: 'Hello Thingtime!'
|
||||
},
|
||||
cache: {
|
||||
revalidate: 60
|
37
remix/app/routes/api/v1/login/_login.tsx
Normal file
37
remix/app/routes/api/v1/login/_login.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { checkUserExists } from "~/api/utils/checkUserExists";
|
||||
|
||||
export default function Index() {
|
||||
return <div>Login</div>;
|
||||
}
|
||||
|
||||
export const action = async ({ request }) => {
|
||||
|
||||
console.log('nik request', request);
|
||||
|
||||
// get remix action body
|
||||
|
||||
const body = await request.json();
|
||||
|
||||
const { username, password } = body;
|
||||
|
||||
console.log('nik body', body)
|
||||
|
||||
console.log('nik username', username);
|
||||
console.log('nik password', password);
|
||||
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: {
|
||||
message: 'Hello, World!',
|
||||
username,
|
||||
password
|
||||
},
|
||||
cache: {
|
||||
revalidate: 60
|
||||
}
|
||||
};
|
||||
};
|
17
remix/app/routes/login.tsx
Normal file
17
remix/app/routes/login.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { Login } from '~/components/Login/Login';
|
||||
import { useApi } from '~/hooks/useApi';
|
||||
|
||||
export default function login() {
|
||||
const template = (
|
||||
<>
|
||||
<Flex alignItems="center" justifyContent="center" width="100%" height="100%" minHeight="100vh">
|
||||
<Login></Login>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
|
||||
return template;
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^2.7.1",
|
||||
"@editorjs/editorjs": "^2.27.2",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
@ -29,6 +30,7 @@
|
||||
"gradient-path": "^2.3.0",
|
||||
"isbot": "latest",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mongodb": "^6.5.0",
|
||||
"react": "^18.2.0",
|
||||
"react-click-away-listener": "^2.2.3",
|
||||
"react-contenteditable": "^3.3.7",
|
||||
@ -59,6 +61,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"remix-flat-routes": "^0.6.4",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^5.1.0",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
// future: {
|
||||
// unstable_dev: true
|
||||
// appServerPort: 3999
|
||||
// }
|
||||
appDirectory: 'src'
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useFetcher } from '@remix-run/react';
|
||||
|
||||
export const useLogin = () => {
|
||||
const fetcher = useFetcher();
|
||||
|
||||
const login = React.useCallback(async (email, password) => {
|
||||
try {
|
||||
fetcher.submit(
|
||||
{ email, password },
|
||||
{
|
||||
method: 'post',
|
||||
action: '/api/v1/login'
|
||||
}
|
||||
);
|
||||
|
||||
// const response = await axios.post('/api/v1/login', { email, password });
|
||||
// console.log('nik response', response.data);
|
||||
// return response.data;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { login };
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
export const action = async ({ request }) => {
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: {
|
||||
message: 'Hello, World!'
|
||||
},
|
||||
cache: {
|
||||
revalidate: 60
|
||||
}
|
||||
};
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
import { Flex } from "@chakra-ui/react"
|
||||
|
||||
import { Login } from "~/components/Login/Login"
|
||||
|
||||
export default function login() {
|
||||
const template = (
|
||||
<>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
width="100%"
|
||||
height="100%"
|
||||
minHeight="100vh"
|
||||
>
|
||||
<Login></Login>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
|
||||
return template
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
|
@ -2,11 +2,10 @@ import { vitePlugin as remix } from '@remix-run/dev';
|
||||
import { installGlobals } from '@remix-run/node';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { flatRoutes } from 'remix-flat-routes'
|
||||
|
||||
installGlobals();
|
||||
|
||||
// set app path to src/
|
||||
|
||||
export default defineConfig({
|
||||
// define web socket port
|
||||
|
||||
@ -18,8 +17,13 @@ export default defineConfig({
|
||||
},
|
||||
plugins: [
|
||||
remix({
|
||||
|
||||
routes: async defineRoutes => {
|
||||
return flatRoutes('routes', defineRoutes)
|
||||
},
|
||||
|
||||
// app path
|
||||
appDirectory: 'src'
|
||||
appDirectory: 'app'
|
||||
}),
|
||||
tsconfigPaths()
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user