chore: get token from google, add the thalos connector and get and persist the thalos token id for future use in other endpoints

This commit is contained in:
Rodolfo Ruiz 2025-08-29 21:14:25 -06:00
parent 339bad77ac
commit 3115d45135
3 changed files with 105 additions and 44 deletions

View File

@ -0,0 +1,59 @@
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
export default function ThalosTokenConnector({ googleIdToken, onSuccess, onError }) {
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!googleIdToken) return;
let cancelled = false;
const run = async () => {
setLoading(true);
try {
const res = await fetch(
'https://thalos-bff.dream-views.com/api/v1/Authentication/GenerateToken',
{
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${googleIdToken}`,
},
}
);
if (!res.ok) {
const text = await res.text();
throw new Error(`Auth exchange failed (${res.status}): ${text || 'No details'}`);
}
const payload = await res.json();
if (cancelled) return;
if (payload?.token) {
localStorage.setItem('thalosToken', payload.token);
}
onSuccess?.(payload);
} catch (err) {
if (!cancelled) onError?.(err);
console.error('Thalos token exchange error:', err);
} finally {
if (!cancelled) setLoading(false);
}
};
run();
return () => {
cancelled = true;
};
}, [googleIdToken, onSuccess, onError]);
return null;
}
ThalosTokenConnector.propTypes = {
googleIdToken: PropTypes.string.isRequired,
onSuccess: PropTypes.func,
onError: PropTypes.func,
};

View File

@ -1,4 +1,3 @@
// src/components/MenuDrawerPrivate.jsx
import React, { useMemo, useState, useEffect } from 'react';
import {
Drawer, List, ListItemButton, ListItemIcon, ListItemText,
@ -6,7 +5,6 @@ import {
} from '@mui/material';
import { useTheme } from '@mui/material/styles';
// MUI icons (you can replace with your PNGs later)
import InsightsIcon from '@mui/icons-material/Insights';
import Inventory2Icon from '@mui/icons-material/Inventory2';
import PeopleAltIcon from '@mui/icons-material/PeopleAlt';
@ -19,7 +17,6 @@ import ExpandMore from '@mui/icons-material/ExpandMore';
const OPEN_WIDTH = 400;
const MINI_WIDTH = 72;
// ---- Hierarchy (from your diagram). Leaves are "CRUD" pages (clickables). ----
const menuData = [
{
title: 'Business Intelligence',
@ -100,28 +97,24 @@ const menuData = [
];
export default function MenuDrawerPrivate({
open, // optional: for mobile temporary drawer
onClose, // optional: for mobile temporary drawer
onSelect, // (title) => void
onExpandedChange, // (boolean) => void // optional: tell header if expanded/collapsed
open,
onClose,
onSelect,
onExpandedChange,
}) {
const theme = useTheme();
const isMobile = useMediaQuery('(max-width:900px)');
const [collapsed, setCollapsed] = useState(false);
// open states per branch key
const [openMap, setOpenMap] = useState({});
// keep collapsed in sync only if "open" prop provided (mobile)
useEffect(() => {
if (!isMobile) return;
if (typeof open === 'boolean') {
// temporary drawer: collapsed UI is not shown on mobile, treat as expanded
setCollapsed(false);
}
}, [open, isMobile]);
// inform parent of expanded/collapsed (desktop)
useEffect(() => {
onExpandedChange?.(isMobile ? true : !collapsed);
}, [collapsed, isMobile, onExpandedChange]);
@ -131,7 +124,6 @@ export default function MenuDrawerPrivate({
const toggleCollapse = () => setCollapsed(c => !c);
const handleToggleNode = (key) => {
// if rail collapsed, expand rail first to reveal submenu
if (!isMobile && collapsed) {
setCollapsed(false);
setOpenMap((m) => ({ ...m, [key]: true }));

View File

@ -1,46 +1,56 @@
import React, { useState } from 'react';
import { GoogleLogin } from '@react-oauth/google';
import { jwtDecode } from 'jwt-decode';
import ThalosTokenConnector from '../auth/ThalosTokenConnector';
import { useAuth } from '../context/AuthContext';
import { Box, Paper, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { Box, Typography } from '@mui/material';
export default function LoginPage() {
const [googleIdToken, setGoogleIdToken] = useState(null);
const [googleProfile, setGoogleProfile] = useState(null);
const { login } = useAuth();
const navigate = useNavigate();
const handleSuccess = (credentialResponse) => {
try {
const token = credentialResponse.credential;
const decoded = jwtDecode(token);
console.log('Google user decoded:', decoded);
// save user in context
login({
name: decoded.name,
email: decoded.email,
picture: decoded.picture,
token,
});
console.log('User logged in and saved to context token:', token);
} catch (err) {
console.error('Token decode failed:', err);
}
};
const handleError = () => {
console.error('Google login failed');
};
return (
<Box display="flex" flexDirection="column" alignItems="center" mt={10}>
<Typography variant="h4" gutterBottom>
Sign in with Google
</Typography>
<Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
<Paper sx={{ p: 4, borderRadius: 2, boxShadow: 3, textAlign: 'center' }}>
<Typography variant="h5" mb={2}>Login with Google</Typography>
<GoogleLogin
onSuccess={handleSuccess}
onError={handleError}
/>
<GoogleLogin
onSuccess={(cred) => {
const idToken = cred?.credential;
if (!idToken) return;
const decoded = jwtDecode(idToken);
setGoogleIdToken(idToken);
setGoogleProfile({
name: decoded?.name || '',
email: decoded?.email || '',
picture: decoded?.picture || '',
});
}}
onError={() => console.error('Google login failed')}
/>
{googleIdToken && (
<ThalosTokenConnector
googleIdToken={googleIdToken}
onSuccess={(payload) => {
login({
...googleProfile,
idToken: googleIdToken,
thalosToken: payload?.token || '',
});
navigate('/');
}}
onError={(err) => {
console.error('Thalos exchange failed:', err);
localStorage.removeItem('thalosToken');
}}
/>
)}
</Paper>
</Box>
);
}