Skip to content

Commit

Permalink
(FE) Add Retrieving a User Information (#70)
Browse files Browse the repository at this point in the history
* Add react query

* Add axios deps

* Save user data in store

* Add protected routes

* Add workspace page

* Add lastWorksapceId to user controller

* Change routes paths

* Add workspace page

* Add retrieving a user information
  • Loading branch information
devleejb committed Jan 19, 2024
1 parent 01317fb commit 65157f4
Show file tree
Hide file tree
Showing 20 changed files with 490 additions and 40 deletions.
2 changes: 1 addition & 1 deletion backend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { ValidationPipe } from "@nestjs/common";

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create(AppModule, { cors: true });

// Swagger
const document = SwaggerModule.createDocument(
Expand Down
2 changes: 2 additions & 0 deletions backend/src/users/types/user-domain.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export class UserDomain {
id: string;
@ApiProperty({ type: String, description: "Nickname of user" })
nickname: string;
@ApiProperty({ type: String, description: "Last worksace ID of user" })
lastWorkspaceId: string;
@ApiProperty({ type: Date, description: "Created date of user" })
createdAt: Date;
@ApiProperty({ type: Date, description: "Updated date of user" })
Expand Down
48 changes: 44 additions & 4 deletions backend/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,26 @@ import { Injectable } from "@nestjs/common";
import { User } from "@prisma/client";
import { PrismaService } from "src/db/prisma.service";
import { FindUserResponse } from "./types/find-user-response.type";
import { WorkspaceRoleConstants } from "src/utils/constants/auth-role";

@Injectable()
export class UsersService {
constructor(private prismaService: PrismaService) {}

async findOne(userId: string): Promise<FindUserResponse> {
return await this.prismaService.user.findUnique({
const foundUserWorkspace = await this.prismaService.userWorkspace.findFirst({
select: {
workspaceId: true,
},
where: {
userId,
},
orderBy: {
id: "desc",
},
});

const foundUser = await this.prismaService.user.findUnique({
select: {
id: true,
nickname: true,
Expand All @@ -19,24 +32,51 @@ export class UsersService {
id: userId,
},
});

return {
...foundUser,
lastWorkspaceId: foundUserWorkspace.workspaceId,
};
}

async findOrCreate(
socialProvider: string,
socialUid: string,
nickname: string
): Promise<User | null> {
return this.prismaService.user.upsert({
const foundUser = await this.prismaService.user.findFirst({
where: {
socialProvider,
socialUid,
},
update: {},
create: {
});

if (foundUser) {
return foundUser;
}

const user = await this.prismaService.user.create({
data: {
socialProvider,
socialUid,
nickname,
},
});

const workspace = await this.prismaService.workspace.create({
data: {
title: `${user.nickname}'s Workspace`,
},
});

await this.prismaService.userWorkspace.create({
data: {
userId: user.id,
workspaceId: workspace.id,
role: WorkspaceRoleConstants.OWNER,
},
});

return user;
}
}
116 changes: 116 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
"@react-hook/window-size": "^3.1.1",
"@reduxjs/toolkit": "^2.0.1",
"@swc/helpers": "^0.5.3",
"@tanstack/react-query": "^5.17.15",
"@uiw/codemirror-theme-xcode": "^4.21.21",
"@uiw/codemirror-themes": "^4.21.21",
"@uiw/react-markdown-preview": "^5.0.7",
"axios": "^1.6.5",
"codemirror": "^6.0.1",
"codemirror-markdown-commands": "^0.0.3",
"codemirror-markdown-slug": "^0.0.3",
Expand Down
36 changes: 5 additions & 31 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,14 @@ import "./App.css";
import { Box, CssBaseline, ThemeProvider, createTheme, useMediaQuery } from "@mui/material";
import { useSelector } from "react-redux";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import EditorLayout from "./components/layouts/EditorLayout";
import EditorIndex from "./pages/editor/Index";
import { useMemo } from "react";
import { selectConfig } from "./store/configSlice";
import MainLayout from "./components/layouts/MainLayout";
import Index from "./pages/Index";
import CallbackIndex from "./pages/auth/callback/Index";
import axios from "axios";
import { routes } from "./routes";

const router = createBrowserRouter([
{
path: "",
element: <MainLayout />,
children: [
{
path: "",
element: <Index />,
},
],
},
{
path: ":documentId",
element: <EditorLayout />,
children: [
{
path: "",
element: <EditorIndex />,
},
],
},
{
path: "auth/callback",
element: <CallbackIndex />,
},
]);
const router = createBrowserRouter(routes);

axios.defaults.baseURL = import.meta.env.VITE_API_ADDR;

function App() {
const config = useSelector(selectConfig);
Expand Down
30 changes: 30 additions & 0 deletions frontend/src/components/common/GuestRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ReactNode, useContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/AuthContext";
import { useSelector } from "react-redux";
import { selectUser } from "../../store/userSlice";

interface RejectLoggedInRouteProps {
children?: ReactNode;
}

const GuestRoute = (props: RejectLoggedInRouteProps) => {
const { children } = props;
const { isLoggedIn } = useContext(AuthContext);
const location = useLocation();
const userStore = useSelector(selectUser);

if (isLoggedIn) {
return (
<Navigate
to={`/workspace/${userStore.data?.lastWorkspaceId}`}
state={{ from: location }}
replace
/>
);
}

return children;
};

export default GuestRoute;
30 changes: 30 additions & 0 deletions frontend/src/components/common/PrivateRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ReactNode, useContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/AuthContext";
import { Backdrop, CircularProgress } from "@mui/material";

interface PrivateRouteProps {
children?: ReactNode;
}

const PrivateRoute = (props: PrivateRouteProps) => {
const { children } = props;
const { isLoggedIn, isLoading } = useContext(AuthContext);
const location = useLocation();

if (isLoading) {
return (
<Backdrop open>
<CircularProgress color="inherit" />
</Backdrop>
);
}

if (!isLoggedIn) {
return <Navigate to="/" state={{ from: location }} replace />;
}

return children;
};

export default PrivateRoute;
Loading

0 comments on commit 65157f4

Please sign in to comment.