Compare commits
4 Commits
ef9ae99cb6
...
75ff63a529
| Author | SHA1 | Date | |
|---|---|---|---|
| 75ff63a529 | |||
| b04ee4cb92 | |||
| 4e613554e6 | |||
| d1718becde |
@@ -67,7 +67,7 @@ class AuthenticationBackendMe(AuthenticationBackend):
|
|||||||
|
|
||||||
class CookieTransportOauth(CookieTransport):
|
class CookieTransportOauth(CookieTransport):
|
||||||
async def get_login_response(self, token: str) -> Response:
|
async def get_login_response(self, token: str) -> Response:
|
||||||
response = RedirectResponse("/login?oauth=success", status_code=status.HTTP_301_MOVED_PERMANENTLY)
|
response = RedirectResponse("/auth/login?oauth=success", status_code=status.HTTP_301_MOVED_PERMANENTLY)
|
||||||
return self._set_login_cookie(response, token)
|
return self._set_login_cookie(response, token)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -5,25 +5,29 @@ from pymongo import IndexModel
|
|||||||
from hub.core import CrudDocument
|
from hub.core import CrudDocument
|
||||||
|
|
||||||
class Firm(CrudDocument):
|
class Firm(CrudDocument):
|
||||||
name: str = Field()
|
|
||||||
instance: str = Field()
|
instance: str = Field()
|
||||||
|
firm: str = Field()
|
||||||
owner: PydanticObjectId = Field()
|
owner: PydanticObjectId = Field()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_name(cls, instance, firm):
|
||||||
|
return cls.find_one({"instance": instance, "firm": firm})
|
||||||
|
|
||||||
def compute_label(self) -> str:
|
def compute_label(self) -> str:
|
||||||
return self.name
|
return f"{self.instance} / {self.firm}"
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
indexes = [
|
indexes = [
|
||||||
IndexModel(["name", "instance"], unique=True),
|
IndexModel(["instance", "firm"], unique=True),
|
||||||
]
|
]
|
||||||
|
|
||||||
class FirmRead(BaseModel):
|
class FirmRead(BaseModel):
|
||||||
instance: str = Field()
|
instance: str = Field()
|
||||||
name: str = Field()
|
firm: str = Field()
|
||||||
|
|
||||||
class FirmCreate(FirmRead):
|
class FirmCreate(FirmRead):
|
||||||
instance: str = Field(max_length=32, min_length=3, pattern="^[0-9a-z-]+$")
|
instance: str = Field(max_length=32, min_length=3, pattern="^[0-9a-z-]+$")
|
||||||
name: str = Field(max_length=32, min_length=3, pattern="^[0-9a-z-]+$")
|
firm: str = Field(max_length=32, min_length=3, pattern="^[0-9a-z-]+$")
|
||||||
|
|
||||||
class FirmUpdate(BaseModel):
|
class FirmUpdate(BaseModel):
|
||||||
owner: PydanticObjectId = Field()
|
owner: PydanticObjectId = Field()
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from beanie import PydanticObjectId
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
|
||||||
from hub.auth import get_current_user
|
from hub.auth import get_current_user
|
||||||
@@ -7,42 +6,41 @@ from hub.firm import Firm, FirmRead, FirmCreate, FirmUpdate
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/", response_model=list[FirmRead], response_description="{} records retrieved".format(Firm.__name__))
|
@router.get("/", response_model=list[FirmRead], response_description="List of firms owned by the current user")
|
||||||
async def read_list(user=Depends(get_current_user)) -> list[FirmRead]:
|
async def read_list(user=Depends(get_current_user)) -> list[FirmRead]:
|
||||||
return await Firm.find({ "owner": user.id}).to_list()
|
return await Firm.find({ "owner": user.id}).to_list()
|
||||||
|
|
||||||
@router.post("/", response_description="{} added to the database".format(Firm.__name__))
|
@router.post("/", response_description="Firm added to the database")
|
||||||
async def create(item: FirmCreate, user=Depends(get_current_user)) -> FirmRead:
|
async def create(item: FirmCreate, user=Depends(get_current_user)) -> FirmRead:
|
||||||
firm_dict = {"name": item.name, "instance": item.instance}
|
exists = await Firm.get_by_name(item.instance, item.firm)
|
||||||
exists = await Firm.find_one(firm_dict)
|
|
||||||
if exists:
|
if exists:
|
||||||
raise HTTPException(status_code=400, detail="Firm already exists")
|
raise HTTPException(status_code=400, detail="Firm already exists")
|
||||||
|
|
||||||
record = Firm(created_by=user.id, updated_by=user.id, owner=user.id, **item.model_dump())
|
record = Firm(created_by=user.id, updated_by=user.id, owner=user.id, **item.model_dump())
|
||||||
o = await record.create()
|
o = await record.create()
|
||||||
user.firms.append(firm_dict)
|
user.firms.append({"instance": item.instance, "firm": item.firm})
|
||||||
await user.save()
|
await user.save()
|
||||||
return FirmRead(**o.model_dump())
|
return FirmRead(**o.model_dump())
|
||||||
|
|
||||||
@router.get("/{id}", response_description="{} record retrieved".format(Firm.__name__))
|
@router.get("/{instance}/{firm}", response_description="Firm retrieved")
|
||||||
async def read_id(id: PydanticObjectId, user=Depends(get_current_user)) -> FirmRead:
|
async def read_id(instance: str, firm: str, user=Depends(get_current_user)) -> FirmRead:
|
||||||
item = await Firm.get(id)
|
item = await Firm.get_by_name(instance, firm)
|
||||||
|
if not item or not user.belong_to(item) not in user.firms:
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
return FirmRead(**item.model_dump())
|
return FirmRead(**item.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{id}", response_description="{} record updated".format(Firm.__name__))
|
@router.put("/{instance}/{firm}", response_description="Firm updated")
|
||||||
async def update(id: PydanticObjectId, req: FirmUpdate, user=Depends(get_current_user)) -> FirmRead:
|
async def update(instance: str, firm: str, req: FirmUpdate, user=Depends(get_current_user)) -> FirmRead:
|
||||||
item = await Firm.get(id)
|
item = await Firm.get_by_name(instance, firm)
|
||||||
if not item:
|
if not item or not user.belong_to(item) not in user.firms:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
status_code=404,
|
|
||||||
detail="{} record not found!".format(Firm.__name__)
|
|
||||||
)
|
|
||||||
|
|
||||||
if item.owner != user.id:
|
if item.owner != user.id:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=403,
|
status_code=403,
|
||||||
detail="Insufficient credentials to modify {} record".format(Firm.__name__)
|
detail="Insufficient credentials to modify Firm"
|
||||||
)
|
)
|
||||||
|
|
||||||
req = {k: v for k, v in req.model_dump().items() if v is not None}
|
req = {k: v for k, v in req.model_dump().items() if v is not None}
|
||||||
@@ -53,21 +51,19 @@ async def update(id: PydanticObjectId, req: FirmUpdate, user=Depends(get_current
|
|||||||
await item.update(update_query)
|
await item.update(update_query)
|
||||||
return FirmRead(**item.dict())
|
return FirmRead(**item.dict())
|
||||||
|
|
||||||
@router.delete("/{id}", response_description="{} record deleted from the database".format(Firm.__name__))
|
@router.delete("/{instance}/{firm}", response_description="Firm deleted from the database")
|
||||||
async def delete(id: PydanticObjectId, user=Depends(get_current_user)) -> dict:
|
async def delete(instance: str, firm: str, user=Depends(get_current_user)) -> dict:
|
||||||
item = await Firm.get(id)
|
item = await Firm.get_by_name(instance, firm)
|
||||||
if not item:
|
if not item or not user.belong_to(item) not in user.firms:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail="Firm not found")
|
||||||
status_code=404,
|
|
||||||
detail="{} record not found!".format(Firm.__name__)
|
|
||||||
)
|
|
||||||
if item.owner != user.id:
|
if item.owner != user.id:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=403,
|
status_code=403,
|
||||||
detail="Insufficient credentials delete {} record".format(Firm.__name__)
|
detail="Insufficient credentials delete Firm"
|
||||||
)
|
)
|
||||||
|
|
||||||
await item.delete()
|
await item.delete()
|
||||||
return {
|
return {
|
||||||
"message": "{} deleted successfully".format(Firm.__name__)
|
"message": "Firm deleted successfully"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,11 @@ from hub.firm import FirmRead
|
|||||||
class UserSchema(BaseUser[PydanticObjectId]):
|
class UserSchema(BaseUser[PydanticObjectId]):
|
||||||
firms: list[FirmRead] = Field()
|
firms: list[FirmRead] = Field()
|
||||||
|
|
||||||
|
def belongs_to(self, firm):
|
||||||
|
for f in self.firms:
|
||||||
|
if f.instance == firm.instance and f.firm == firm.firm :
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
class UserUpdateSchema(BaseUserUpdate):
|
class UserUpdateSchema(BaseUserUpdate):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import { ForgotPassword } from "./components/auth/ForgotPassword";
|
|||||||
import { UpdatePassword } from "./components/auth/UpdatePassword";
|
import { UpdatePassword } from "./components/auth/UpdatePassword";
|
||||||
|
|
||||||
import { Header } from "./components";
|
import { Header } from "./components";
|
||||||
import { Hub } from "./pages/hub";
|
import { HubRoutes } from "./pages/hub";
|
||||||
import { CreateFirm } from "./pages/hub/CreateFirm";
|
import { FirmRoutes } from "./pages/firm";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -43,26 +43,42 @@ function App() {
|
|||||||
syncWithLocation: true,
|
syncWithLocation: true,
|
||||||
warnWhenUnsavedChanges: true,
|
warnWhenUnsavedChanges: true,
|
||||||
useNewQueryKeys: true,
|
useNewQueryKeys: true,
|
||||||
disableTelemetry: true
|
disableTelemetry: true,
|
||||||
|
reactQuery: {
|
||||||
|
clientConfig: {
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
retry: (failureCount, error) => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (error.status >= 400 && error.status <= 499) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return failureCount < 4
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Header />
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
element={(
|
element={(
|
||||||
<Authenticated key="authenticated-routes" redirectOnFail="/login" fallback={<CatchAllNavigate to="/login"/>}>
|
<Authenticated key="authenticated-routes" redirectOnFail="/auth/login" fallback={<CatchAllNavigate to="/auth/login"/>}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Authenticated>
|
</Authenticated>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Route path="/hub" element={ <Hub /> } />
|
<Route path="hub/*" element={<HubRoutes />} />
|
||||||
<Route path="/hub/create-firm" element={ <CreateFirm /> } />
|
<Route path="firm/*" element={<FirmRoutes />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route index element={<h1>HOME <Link to={"/login"}>Login</Link></h1>} />
|
<Route path="auth/*" element={<><Header /><Outlet /></>}>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="login" element={<Login />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<Route path="register" element={<Register />} />
|
||||||
<Route path="/forgot-password" element={<ForgotPassword />} />
|
<Route path="forgot-password" element={<ForgotPassword />} />
|
||||||
<Route path="/update-password" element={<UpdatePassword />} />
|
<Route path="update-password" element={<UpdatePassword />} />
|
||||||
|
</Route>
|
||||||
|
<Route index element={<><Header /><h1>HOME </h1></>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
<UnsavedChangesNotifier />
|
<UnsavedChangesNotifier />
|
||||||
<DocumentTitleHandler />
|
<DocumentTitleHandler />
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { Button } from "@mui/material";
|
||||||
import { useLogout } from "@refinedev/core";
|
import { useLogout } from "@refinedev/core";
|
||||||
|
|
||||||
export const Logout = () => {
|
export const Logout = () => {
|
||||||
const { mutate: logout } = useLogout();
|
const { mutate: logout } = useLogout();
|
||||||
|
|
||||||
return <button onClick={() => logout()} >Logout</button>;
|
return <Button onClick={() => logout()} >Logout</Button>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
||||||
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
||||||
|
import HubIcon from '@mui/icons-material/Hub';
|
||||||
|
import { Button, Menu, MenuItem } from "@mui/material";
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from "@mui/material/AppBar";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import { Link } from "react-router";
|
||||||
import { useGetIdentity } from "@refinedev/core";
|
import { useGetIdentity } from "@refinedev/core";
|
||||||
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
|
import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from "@refinedev/mui";
|
||||||
import React, { useContext } from "react";
|
|
||||||
import { ColorModeContext } from "../../contexts/color-mode";
|
import { ColorModeContext } from "../../contexts/color-mode";
|
||||||
|
import { FirmContext } from "../../contexts/FirmContext";
|
||||||
import { Logout } from "../auth/Logout";
|
import { Logout } from "../auth/Logout";
|
||||||
import { IUser } from "../../interfaces";
|
import { IUser } from "../../interfaces";
|
||||||
|
|
||||||
@@ -17,34 +21,41 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
|||||||
sticky = true,
|
sticky = true,
|
||||||
}) => {
|
}) => {
|
||||||
const { mode, setMode } = useContext(ColorModeContext);
|
const { mode, setMode } = useContext(ColorModeContext);
|
||||||
|
const { currentFirm } = useContext(FirmContext);
|
||||||
|
|
||||||
const { data: user } = useGetIdentity<IUser>();
|
const { data: user } = useGetIdentity<IUser>();
|
||||||
|
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const openUserMenu = Boolean(anchorEl);
|
||||||
|
const handleOpenUserMenu = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
}
|
||||||
|
const handleCloseUserMenu = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position={sticky ? "sticky" : "relative"}>
|
<AppBar position={sticky ? "sticky" : "relative"}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Stack
|
<Stack
|
||||||
direction="row"
|
direction="row"
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="flex-end"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<HamburgerMenu />
|
<HamburgerMenu />
|
||||||
|
{currentFirm && (
|
||||||
|
<Link to={`/firm/${currentFirm.instance}/${currentFirm.firm}`} ><Typography variant="h4" >{currentFirm.instance} / {currentFirm.firm}</Typography></Link>
|
||||||
|
)}
|
||||||
|
{!currentFirm && (
|
||||||
|
<Link to="/" ><Typography variant="h4">Roleplay Contracts</Typography></Link>
|
||||||
|
)}
|
||||||
|
<Link to="/hub"><HubIcon /></Link>
|
||||||
<Stack
|
<Stack
|
||||||
direction="row"
|
direction="row"
|
||||||
width="100%"
|
|
||||||
justifyContent="flex-end"
|
justifyContent="flex-end"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<IconButton
|
|
||||||
color="inherit"
|
|
||||||
onClick={() => {
|
|
||||||
setMode();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
{(user?.email) && (
|
{(user?.email) && (
|
||||||
<Stack
|
<Stack
|
||||||
direction="row"
|
direction="row"
|
||||||
@@ -52,7 +63,12 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
{user?.email && (
|
<Button
|
||||||
|
id="user-menu-button"
|
||||||
|
aria-controls={openUserMenu ? 'user-menu' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={openUserMenu ? 'true' : undefined}
|
||||||
|
onClick={handleOpenUserMenu}>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
display: {
|
display: {
|
||||||
@@ -63,12 +79,36 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
|
|||||||
variant="subtitle2"
|
variant="subtitle2"
|
||||||
>
|
>
|
||||||
{user?.email}
|
{user?.email}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
<Avatar src={"user?.avatar"} alt={user?.email} />
|
||||||
<Avatar src={"user?.avatar"} alt={user?.email} />
|
</Button>
|
||||||
<Logout />
|
<Menu
|
||||||
|
id="user-menu"
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={openUserMenu}
|
||||||
|
onClose={handleCloseUserMenu}
|
||||||
|
MenuListProps={{
|
||||||
|
'aria-labelledby': 'user-menu-button',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleCloseUserMenu}><Logout /></MenuItem>
|
||||||
|
<MenuItem onClick={handleCloseUserMenu}>
|
||||||
|
<IconButton
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => {
|
||||||
|
setMode();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
|
||||||
|
</IconButton>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
{!user && (
|
||||||
|
<Link to="/auth/login"><Button>Login</Button></Link>
|
||||||
|
)}
|
||||||
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|||||||
25
gui/rpk-gui/src/contexts/FirmContext.tsx
Normal file
25
gui/rpk-gui/src/contexts/FirmContext.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React, { createContext, PropsWithChildren } from 'react';
|
||||||
|
import { IFirm } from "../interfaces";
|
||||||
|
import { useParams } from "react-router";
|
||||||
|
|
||||||
|
type FirmContextType = {
|
||||||
|
currentFirm: IFirm,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FirmContext = createContext<FirmContextType>(
|
||||||
|
{} as FirmContextType
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export const FirmContextProvider: React.FC<PropsWithChildren> = ({ children }: PropsWithChildren) => {
|
||||||
|
const { instance, firm } = useParams<IFirm>()
|
||||||
|
|
||||||
|
if (instance === undefined || firm === undefined) {
|
||||||
|
return "Error"
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FirmContext.Provider value={{currentFirm: {instance, firm}}} >
|
||||||
|
{ children }
|
||||||
|
</FirmContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
export type IFirm = {
|
export type IFirm = {
|
||||||
instance: string,
|
instance: string,
|
||||||
name: string
|
firm: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type User = {
|
type User = {
|
||||||
|
|||||||
28
gui/rpk-gui/src/pages/firm/index.tsx
Normal file
28
gui/rpk-gui/src/pages/firm/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import {Route, Routes} from "react-router";
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import { FirmContext, FirmContextProvider } from "../../contexts/FirmContext";
|
||||||
|
import { Header } from "../../components";
|
||||||
|
|
||||||
|
export const FirmRoutes = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/:instance/:firm/*" element={
|
||||||
|
<FirmContextProvider>
|
||||||
|
<Header />
|
||||||
|
<Routes>
|
||||||
|
<Route index element={ <FirmHome /> } />
|
||||||
|
</Routes>
|
||||||
|
</FirmContextProvider>
|
||||||
|
} />
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FirmHome = () => {
|
||||||
|
const { currentFirm } = useContext(FirmContext);
|
||||||
|
return (
|
||||||
|
<h1>This is la firme {currentFirm.instance} / {currentFirm.firm}</h1>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,38 +1,53 @@
|
|||||||
import { Button } from "@mui/material";
|
import { Button } from "@mui/material";
|
||||||
import { Link } from "react-router";
|
import ExitToAppIcon from '@mui/icons-material/ExitToApp';
|
||||||
|
import React from 'react';
|
||||||
|
import {Link, Route, Routes} from "react-router";
|
||||||
import { useGetIdentity, useList } from "@refinedev/core";
|
import { useGetIdentity, useList } from "@refinedev/core";
|
||||||
import { IAuthUser, IFirm } from "../../interfaces";
|
import { IAuthUser, IFirm } from "../../interfaces";
|
||||||
|
import {CreateFirm} from "./CreateFirm";
|
||||||
|
import {Header} from "../../components";
|
||||||
|
|
||||||
|
|
||||||
export const Hub = () => {
|
export const HubRoutes = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<Routes>
|
||||||
|
<Route index element={ <HubHome /> } />
|
||||||
|
<Route path="create-firm" element={ <CreateFirm /> } />
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const HubHome = () => {
|
||||||
const { data: user } = useGetIdentity<IAuthUser>();
|
const { data: user } = useGetIdentity<IAuthUser>();
|
||||||
const { data: list } = useList<IFirm>({resource: "hub/users/firms/", pagination: { mode: "off" }}, )
|
const { data: list } = useList<IFirm>({resource: "hub/users/firms/", pagination: { mode: "off" }}, )
|
||||||
if (user === undefined || list === undefined) {
|
if (user === undefined || list === undefined) {
|
||||||
return <p>Loading</p>
|
return <p>Loading</p>;
|
||||||
}
|
}
|
||||||
console.log("list data: ", list);
|
|
||||||
const ownedFirms = list.data;
|
const ownedFirms = list.data;
|
||||||
if (user === undefined || ownedFirms === undefined) {
|
if (user === undefined || ownedFirms === undefined) {
|
||||||
return <p>Loading</p>
|
return <p>Loading</p>
|
||||||
}
|
}
|
||||||
console.log("owned firms: ", ownedFirms);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>HUB</h1>
|
<h1>HUB</h1>
|
||||||
<p>List of managed firms</p>
|
<p>List of managed firms</p>
|
||||||
<ul>
|
<ul>
|
||||||
{ownedFirms.map((f: IFirm, index) => (
|
{ownedFirms.map((f: IFirm, index) => (
|
||||||
<li key={index}>{f.instance} / {f.name}</li>
|
<li key={index}>{f.instance} / {f.firm}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
<Link to="/hub/create-firm" ><Button>Create a new firm</Button></Link>
|
||||||
<p>List of firm you're working at</p>
|
<p>List of firm you're working at</p>
|
||||||
<ul>
|
<ul>
|
||||||
{user.firms.map((f: IFirm, index) => (
|
{user.firms.map((f: IFirm, index) => (
|
||||||
<li key={index}>{f.instance} / {f.name}</li>
|
<li key={index}>
|
||||||
|
{f.instance} / {f.firm} <Link to={`/firm/${f.instance}/${f.firm}`}><ExitToAppIcon /></Link>
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<Link to="/hub/create-firm" ><Button >Create a new firm</Button></Link>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ const LOCAL_STORAGE_USER_KEY = "rpk-gui-current-user";
|
|||||||
const GOOGLE_SCOPES = { "scopes": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email" };
|
const GOOGLE_SCOPES = { "scopes": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email" };
|
||||||
const DISCORD_SCOPES = { "scopes": "identify email" }
|
const DISCORD_SCOPES = { "scopes": "identify email" }
|
||||||
|
|
||||||
|
const DEFAULT_LOGIN_REDIRECT = "/hub"
|
||||||
|
|
||||||
export const authProvider: AuthProvider = {
|
export const authProvider: AuthProvider = {
|
||||||
login: async ({ providerName, email, password }) => {
|
login: async ({ providerName, email, password }) => {
|
||||||
const to_param = findGetParameter("to");
|
const to_param = findGetParameter("to");
|
||||||
|
const redirect = to_param === null ? DEFAULT_LOGIN_REDIRECT : to_param
|
||||||
if (providerName) {
|
if (providerName) {
|
||||||
let scope = {};
|
let scope = {};
|
||||||
if (providerName === "google") {
|
if (providerName === "google") {
|
||||||
@@ -22,11 +25,14 @@ export const authProvider: AuthProvider = {
|
|||||||
const response = await fetch(url, { method: "GET", },);
|
const response = await fetch(url, { method: "GET", },);
|
||||||
const body = await response.json();
|
const body = await response.json();
|
||||||
|
|
||||||
if (to_param) {
|
|
||||||
localStorage.setItem("redirect_after_login", to_param);
|
localStorage.setItem("redirect_after_login", redirect);
|
||||||
}
|
|
||||||
window.location.href = body.authorization_url;
|
window.location.href = body.authorization_url;
|
||||||
return { success: true };
|
return {
|
||||||
|
success: true,
|
||||||
|
redirectTo: ""
|
||||||
|
};
|
||||||
} else if (email !== undefined && password !== undefined) {
|
} else if (email !== undefined && password !== undefined) {
|
||||||
const params = new URLSearchParams({"grant_type": "password", "username": email, "password": password});
|
const params = new URLSearchParams({"grant_type": "password", "username": email, "password": password});
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -42,7 +48,10 @@ export const authProvider: AuthProvider = {
|
|||||||
const user = await response.json();
|
const user = await response.json();
|
||||||
store_user(user);
|
store_user(user);
|
||||||
|
|
||||||
return { success: true };
|
return {
|
||||||
|
success: true,
|
||||||
|
redirectTo: redirect,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +61,10 @@ export const authProvider: AuthProvider = {
|
|||||||
const response = await fetch(`${API_URL}/hub/auth/logout`, { method: "POST" });
|
const response = await fetch(`${API_URL}/hub/auth/logout`, { method: "POST" });
|
||||||
if (response.status == 204 || response.status == 401) {
|
if (response.status == 204 || response.status == 401) {
|
||||||
forget_user();
|
forget_user();
|
||||||
return { success: true };
|
return {
|
||||||
|
success: true,
|
||||||
|
redirectTo: "/",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { success: false };
|
return { success: false };
|
||||||
},
|
},
|
||||||
@@ -60,7 +72,7 @@ export const authProvider: AuthProvider = {
|
|||||||
if (get_user() == null) {
|
if (get_user() == null) {
|
||||||
return {
|
return {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
redirectTo: "/login",
|
redirectTo: "/auth/login",
|
||||||
logout: true
|
logout: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +154,7 @@ export const authProvider: AuthProvider = {
|
|||||||
if (error?.status === 401) {
|
if (error?.status === 401) {
|
||||||
forget_user();
|
forget_user();
|
||||||
return {
|
return {
|
||||||
redirectTo: "/login",
|
redirectTo: "/auth/login",
|
||||||
logout: true,
|
logout: true,
|
||||||
error: { message: "Authentication required" },
|
error: { message: "Authentication required" },
|
||||||
} as OnErrorResponse;
|
} as OnErrorResponse;
|
||||||
|
|||||||
Reference in New Issue
Block a user