aboutsummaryrefslogtreecommitdiff
path: root/jb-ui/src
diff options
context:
space:
mode:
authorMax Nanis2026-09-11 18:16:06 -0700
committerMax Nanis2026-09-11 18:16:06 -0700
commit11d63b171501b9c29398133b795c3bcc1ae85a1b (patch)
tree9ef60f1723357889f8bd72420a0c6f7ae5153fa1 /jb-ui/src
parentedff9381275d90d7be5f078430554ae592a27f5d (diff)
downloadamt-jb-11d63b171501b9c29398133b795c3bcc1ae85a1b.tar.gz
amt-jb-11d63b171501b9c29398133b795c3bcc1ae85a1b.zip
using RTK with session login. start of magic token request
Diffstat (limited to 'jb-ui/src')
-rw-r--r--jb-ui/src/AuthContext.tsx28
-rw-r--r--jb-ui/src/JBApp.tsx4
-rw-r--r--jb-ui/src/models/authAPI.ts23
-rw-r--r--jb-ui/src/pages/Work.tsx103
-rw-r--r--jb-ui/src/store.ts5
5 files changed, 152 insertions, 11 deletions
diff --git a/jb-ui/src/AuthContext.tsx b/jb-ui/src/AuthContext.tsx
new file mode 100644
index 0000000..85bcdb0
--- /dev/null
+++ b/jb-ui/src/AuthContext.tsx
@@ -0,0 +1,28 @@
+// AuthContext.jsx
+import { createContext, useContext, useEffect, useState } from "react";
+
+const AuthContext = createContext(null);
+
+export function AuthProvider({ children }) {
+ const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ fetch("/api/me", { credentials: "include" })
+ .then((res) => (res.ok ? res.json() : null))
+ .then(setUser)
+ .finally(() => setLoading(false));
+ }, []);
+
+ return (
+ <AuthContext.Provider
+ value={{ user, isLoggedIn: !!user, loading, setUser }}
+ >
+ {children}
+ </AuthContext.Provider>
+ );
+}
+
+export function useAuth() {
+ return useContext(AuthContext);
+}
diff --git a/jb-ui/src/JBApp.tsx b/jb-ui/src/JBApp.tsx
index 7c99315..fd0eeba 100644
--- a/jb-ui/src/JBApp.tsx
+++ b/jb-ui/src/JBApp.tsx
@@ -225,10 +225,10 @@ function JBApp() {
return (
<BrowserRouter basename={routeBasename}>
<Routes>
- <Route path="/auth/magic-link/" element={<MagicLink />} />
<Route element={<QueryParamProcessor />}>
<Route element={<Layout />}>
- <Route path="/" element={<Home />} />
+ <Route path="/" element={<Work />} />
+ <Route path="/auth/magic-link/" element={<MagicLink />} />
{/* <Route path="/work/" element={<Work />} /> */}
<Route path="/preview/" element={<Preview />} />
diff --git a/jb-ui/src/models/authAPI.ts b/jb-ui/src/models/authAPI.ts
new file mode 100644
index 0000000..1ac7886
--- /dev/null
+++ b/jb-ui/src/models/authAPI.ts
@@ -0,0 +1,23 @@
+import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
+
+export const authApi = createApi({
+ reducerPath: "authApi",
+ baseQuery: fetchBaseQuery({
+ baseUrl: "/auth",
+ credentials: "include", // sends the httpOnly cookie
+ }),
+ tagTypes: ["User"],
+ endpoints: (builder) => ({
+ getMe: builder.query({
+ query: () => "/session",
+ providesTags: ["User"],
+ }),
+ // logout: builder.mutation({
+ // query: () => ({ url: "/de-session", method: "POST" }),
+ // invalidatesTags: ["User"],
+ // }),
+ }),
+});
+
+// export const { useGetMeQuery, useLogoutMutation } = authApi;
+export const { useGetMeQuery } = authApi;
diff --git a/jb-ui/src/pages/Work.tsx b/jb-ui/src/pages/Work.tsx
index 32dcc96..e33328e 100644
--- a/jb-ui/src/pages/Work.tsx
+++ b/jb-ui/src/pages/Work.tsx
@@ -1,3 +1,4 @@
+import { useGetMeQuery } from "@/models/authAPI";
import {
BucketTask,
OfferwallApi,
@@ -828,17 +829,101 @@ const SurveyEnter = () => {
);
};
+type RequestState = "idle" | "working" | "sent" | "failed";
+
+const Login = () => {
+ const [email, setEmail] = useState("");
+ const [state, setState] = useState<RequestState>("idle");
+ const bpuid = useAppSelector((state) => state.app.bpuid);
+
+ // const dispatch = useAppDispatch();
+ const submit = async (event: FormEvent<HTMLFormElement>) => {
+ event.preventDefault();
+ setState("working");
+
+ try {
+ const response = await fetch("/auth/magic-link/request", {
+ method: "POST",
+ credentials: "include",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ email: email,
+ }),
+ });
+
+ if (!response.ok) throw new Error("Account-link request failed");
+
+ const body = (await response.json()) as { magic_link?: string };
+ console.log(body.magic_link ?? null);
+ } catch {
+ setState("failed");
+ }
+ };
+
+ return (
+ <div className="px-6 my-6">
+ <h2
+ className="
+ bg-blue-700 rounded py-2
+ text-white text-2xl font-semibold text-center"
+ >
+ Please Login
+ </h2>
+
+ <form onSubmit={submit}>
+ <input
+ className="w-full rounded-md border-3 border-slate-700 px-3 py-2 font-semibold my-5"
+ type="email"
+ autoComplete="email"
+ required
+ value={email}
+ onChange={(event) => setEmail(event.target.value)}
+ placeholder="hello@jamesbillings67.com"
+ />
+ <Button
+ className="w-1/2 font-semibold bg-slate-600 cursor-pointer hover:bg-blue-950"
+ type="submit"
+ disabled={state === "working"}
+ >
+ {state === "working" ? "Sending…" : "Login"}
+ </Button>
+ </form>
+
+ {state === "sent" && (
+ <h4
+ className="
+ mt-2
+ bg-slate-500 rounded py-2
+ text-white text-l font-semibold text-center"
+ >
+ Check your email to login.
+ </h4>
+ )}
+
+ {state === "failed" && <p>Unable to send the link. Please try again.</p>}
+ </div>
+ );
+};
+
const Work = () => {
const entered = useAppSelector((state) => state.app.currentBucketEntered);
- const assignment_id = useAppSelector((state) => state.app.assignment_id);
-
- if (!assignment_id) {
- return (
- <p className="text-center text-2xl py-3 text-red-800 font-semibold">
- No Assignment ID found. Please access this page through the Amazon
- Mechanical Turk platform.
- </p>
- );
+ // const assignment_id = useAppSelector((state) => state.app.assignment_id);
+ // if (!assignment_id) {
+ // return (
+ // <p className="text-center text-2xl py-3 text-red-800 font-semibold">
+ // No Assignment ID found. Please access this page through the Amazon
+ // Mechanical Turk platform.
+ // </p>
+ // );
+ // }
+
+ const { data: user, isLoading, isError } = useGetMeQuery();
+ // const location = useLocation();
+
+ if (isLoading) return <div>Loading...</div>;
+
+ if (isError || !user) {
+ return <Login />;
}
return <>{entered ? <SurveyMonitor /> : <SurveyEnter />}</>;
diff --git a/jb-ui/src/store.ts b/jb-ui/src/store.ts
index 07cac9e..baf13b7 100644
--- a/jb-ui/src/store.ts
+++ b/jb-ui/src/store.ts
@@ -1,6 +1,8 @@
import appReducers from "@/models/appSlice";
import grlStatsReducers from "@/models/grlStatsSlice";
import grlEventsReducers from "@/models/grlEventsSlice";
+import { authApi } from "@/models/authAPI";
+
import profilingQuestionsReducers from "@/models/profilingQuestionsSlice";
import type { Action, ThunkAction } from "@reduxjs/toolkit";
import { configureStore } from "@reduxjs/toolkit";
@@ -8,10 +10,13 @@ import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {
app: appReducers,
+ // auth: authReducers,
+ [authApi.reducerPath]: authApi.reducer,
stats: grlStatsReducers,
events: grlEventsReducers,
profilingQuestions: profilingQuestionsReducers,
},
+ middleware: (getDefault) => getDefault().concat(authApi.middleware),
});
// Infer the type of `store`