Skip to content

Commit

Permalink
Merge pull request #168 from hexlet-rus/fix-routing
Browse files Browse the repository at this point in the history
Fix routing
  • Loading branch information
dzencot authored Jan 20, 2023
2 parents 81e51dc + b8daa53 commit 7af8c7e
Show file tree
Hide file tree
Showing 19 changed files with 83 additions and 84 deletions.
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"format": "prettier --write \\\"src/**/*.ts\\\" \\\"__test__/**/*.ts\\\"",
"start": "NODE_ENV=development PORT=5001 nest start",
"typeorm": "typeorm-ts-node-commonjs",
"start:debug": "NODE_ENV=development nest start --debug --watch",
"start:debug": "NODE_ENV=development PORT=5001 nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
Expand Down
8 changes: 8 additions & 0 deletions backend/src/snippets/snippets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ export class SnippetsController {
return this.snippetsService.findAll();
}

@Get(':login/:slug')
async findOneByLoginSlug(
@Param('login') login: string,
@Param('slug') slug: string,
): Promise<Snippet> {
return this.snippetsService.findByLoginSlug(login, slug);
}

@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id: number): Promise<Snippet> {
return this.snippetsService.findOne(id);
Expand Down
17 changes: 14 additions & 3 deletions backend/src/snippets/snippets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@ export class SnippetsService {
return this.snippetsRepository.findOneBy({ id });
}

async findByLoginSlug(login: string, slug: string): Promise<any> {
const user = await this.usersRepository.findOneBy({ login });
const snippet = await this.snippetsRepository.findOne({
where: {
user: {
id: user.id,
},
slug,
},
});
return snippet;
}

async getSlug(name: string, login: string, id: number): Promise<string> {
const trimmedName = name.trim();
const extension = path.extname(trimmedName);
const basename = path.basename(trimmedName, extension);
const slug = `${basename
.replace(/\s/g, '-')
.toLowerCase()}_${extension.slice(1)}`;
const slug = `${basename.replace(/\s/g, '-').toLowerCase()}`;
const snippets = await this.snippetManager
.createQueryBuilder(Snippets, 'snippet')
.where('snippet.userId= :id', { id })
Expand Down
2 changes: 1 addition & 1 deletion backend/src/snippets/validation/validation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class ValidationPipe implements PipeTransform<any> {
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
throw new BadRequestException('Validation failed', errors.join(' '));
}
return value;
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/users/validation/validation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class ValidationPipe implements PipeTransform<any> {
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
throw new BadRequestException('Validation failed', errors.join(' '));
}
return value;
}
Expand Down
17 changes: 11 additions & 6 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { useTranslation } from 'react-i18next';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useLoaderData } from 'react-router';
// import { useLoaderData } from 'react-router';
import { useParams } from 'react-router-dom';
import { MonacoEditor } from './components/Editor/index.jsx';
import { Button } from './components/Button/index.jsx';
import { SnippetButton } from './components/SnippetButton/index.jsx';
import { Terminal } from './components/Terminal/index.jsx';
import { actions } from './slices/index.js';
import { useSnippets } from './hooks';

export function App() {
const dispatch = useDispatch();
const snippetApi = useSnippets();
const loaderData = useLoaderData();
const params = useParams();

useEffect(() => {
const loadSnippet = async () => {
if (snippetApi.hasViewSnippetParams(loaderData)) {
const snippetParams = {
login: params.login,
slug: params.slug,
};
if (snippetApi.hasViewSnippetParams(snippetParams)) {
const snippetData = await snippetApi.getSnippetDataByViewParams(
loaderData,
snippetParams,
);
dispatch(actions.updateCode(snippetData.code));
}
Expand All @@ -32,7 +37,7 @@ export function App() {
<main className="container-fluid bg-dark py-5">
<div className="row mb-2">
<div className="col-12">
<Button />
<SnippetButton />
<div className="mt-2 text-center text-muted">
<small>
{isAllSaved ? t('editor.allSaved') : t('editor.unsavedChanges')}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import App from './App';

test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
const linkElement = screen.getByText(/hexlet/i);
expect(linkElement).toBeInTheDocument();
});
1 change: 1 addition & 0 deletions frontend/src/AppRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function AppRoutes() {
<Route path="/" element={<Layout />}>
<Route index element={<Landing />} />
<Route path="/editor" element={<App />} />
<Route path="/users/:login/snippets/:slug" element={<App />} />
<Route path={routes.aboutPagePath()} element={<About />} />
<Route element={<ProtectedRoute user={isLoggedIn} />}>
<Route path={routes.profilePagePath()} element={<Profile />} />
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/Pages/NotFound.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

function NotFound() {
const { t } = useTranslation();

return <div>{t('appRotes.pageNotFound')}</div>;
}

export default NotFound;
4 changes: 2 additions & 2 deletions frontend/src/components/Embed/EmbedRunButton.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useButton } from '../Button/hooks';
import classes from '../Button/Button.module.css';
import { useButton } from '../SnippetButton/hooks';
import classes from '../SnippetButton/SnippetButton.module.css';

export const EmbedRunButton = memo(() => {
const { onClick, disabled } = useButton();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Embed/EmbedSnippet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EmbedRunButton } from './EmbedRunButton.jsx';
import { Terminal } from '../Terminal';
import { actions } from '../../slices/index.js';
import { useSnippets } from '../../hooks';
import classes from '../Button/Button.module.css';
import classes from '../SnippetButton/SnippetButton.module.css';

function EmbedSnippet() {
const snippetApi = useSnippets();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ export const useButton = () => {
() => dispatch(runCode(code)),
[dispatch, runCode, code],
);
const update = async (id) => {
const response = await axios.put(routes.updateSnippetPath(id), { code });
const update = async (id, name) => {
const response = await axios.put(routes.updateSnippetPath(id), {
code,
name,
});
dispatch(actions.updateSavedCode(code));
return response;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import { useTranslation } from 'react-i18next';
import React, { memo, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import classes from './Button.module.css';
import { useParams } from 'react-router-dom';
import classes from './SnippetButton.module.css';
import { useButton } from './hooks';
import { useAuth, useSnippets } from '../../hooks';

import { actions as modalActions } from '../../slices/modalSlice.js';

export const Button = memo(() => {
export const SnippetButton = memo(() => {
const { onClick, disabled, update } = useButton();
const [currentSnippetId, setCurrentSnippetId] = useState();
const dispatch = useDispatch();
const auth = useAuth();
const snippetsApi = useSnippets();
const { t } = useTranslation();
const params = useParams();

useEffect(() => {
if (snippetsApi.hasViewSnippetParams()) {
const decodedId = snippetsApi.getSnippetIdFromParams();
setCurrentSnippetId(decodedId);
} else {
setCurrentSnippetId(false);
}
const getSnippetData = async () => {
const snippetParams = {
login: params.login,
slug: params.slug,
};
if (snippetsApi.hasViewSnippetParams(snippetParams)) {
const snippetData = await snippetsApi.getSnippetDataByViewParams(snippetParams);
setCurrentSnippetId(snippetData.id);
} else {
setCurrentSnippetId(false);
}
};
getSnippetData();
}, []);

const getTypeOfModal = (isLoggedIn) => {
Expand Down Expand Up @@ -65,7 +74,7 @@ export const Button = memo(() => {
disabled={disabled}
onClick={() => {
onClick();
update(currentSnippetId);
update(currentSnippetId, params.slug);
}}
>
{t('editor.runButton')}
Expand Down
52 changes: 0 additions & 52 deletions frontend/src/index.jsx

This file was deleted.

1 change: 0 additions & 1 deletion frontend/src/logo.svg

This file was deleted.

9 changes: 6 additions & 3 deletions frontend/src/providers/SnippetsProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ function SnippetsProvider({ children }) {
};

const getSnippetDataByViewParams = async ({ login, slug }) => {
const { data } = await axios.get(routes.userInfoPath(login));
return data.snippets.find((snippet) => snippet.slug === slug);
const { data } = await axios.get(
routes.getSnippetPathByLoginSlug(login, slug),
);
return data;
};

const saveSnippet = async (code, name) => {
Expand All @@ -51,7 +53,7 @@ function SnippetsProvider({ children }) {
return url.searchParams.has('snippet');
};

const hasViewSnippetParams = (urlData) => {
const hasViewSnippetParams = (urlData = {}) => {
return urlData.login && urlData.slug;
};

Expand All @@ -72,6 +74,7 @@ function SnippetsProvider({ children }) {
};

const genSnippetLink = (encodedId) => {
// TODO: переделать на получение ссылки вида /users/${login}/snippets/${slug}
const url = new URL(routes.homePagePath(), window.location);
url.searchParams.set('snippet', encodedId);
return url.toString();
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default {
snippetsPath: () => [apiPath, 'snippets'].join('/'), // get - shows all snippets
createSnippetPath: () => [apiPath, 'snippets'].join('/'), // post - create snippet: { name, code }
getSnippetPath: (id) => [apiPath, 'snippets', `${id}`].join('/'), // get snippet
getSnippetPathByLoginSlug: (login, slug) =>
[apiPath, 'snippets', login, slug].join('/'),
updateSnippetPath: (id) => [apiPath, 'snippets', `${id}`].join('/'), // put - update snippet info: { name, code }
deleteSnippetPath: (id) => [apiPath, 'snippets', `${id}`].join('/'), // delete snippet
homePagePath: () => '/editor',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/slices/terminalSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import axios from 'axios';
export const runCode = createAsyncThunk(
'terminal/runCode',
async (code) => {
const { data, status } = await axios.get(`api/compile`, {
const { data, status } = await axios.get(`/api/compile`, {
params: { code },
});

Expand Down

0 comments on commit 7af8c7e

Please sign in to comment.