diff --git a/client/src/components/Snippets/SnippetDetails.tsx b/client/src/components/Snippets/SnippetDetails.tsx
index 9de585b..a3cb22e 100644
--- a/client/src/components/Snippets/SnippetDetails.tsx
+++ b/client/src/components/Snippets/SnippetDetails.tsx
@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
import { SnippetsContext } from '../../store';
import { Snippet } from '../../typescript/interfaces';
import { dateParser } from '../../utils';
-import { Button, Card } from '../UI';
+import { Badge, Button, Card } from '../UI';
import copy from 'clipboard-copy';
import { SnippetPin } from './SnippetPin';
@@ -15,6 +15,7 @@ export const SnippetDetails = (props: Props): JSX.Element => {
const {
title,
language,
+ tags,
createdAt,
updatedAt,
description,
@@ -61,6 +62,16 @@ export const SnippetDetails = (props: Props): JSX.Element => {
+ {/* TAGS */}
+
+ {tags.map(tag => (
+
+
+
+ ))}
+
+
+
{/* ACTIONS */}
- Filter by language
+ Filter by tags
- {languageCount.map((el, idx) => {
- const isActiveFilter = filter === el.language;
+ {tagCount.map((tag, idx) => {
+ const isActiveFilter = filter === tag.name;
return (
filterHandler(el.language)}
+ onClick={() => filterHandler(tag.name)}
>
- {el.language}
- {el.count}
+ {tag.name}
+ {tag.count}
);
})}
diff --git a/client/src/store/SnippetsContext.tsx b/client/src/store/SnippetsContext.tsx
index f73d7ee..5d3b305 100644
--- a/client/src/store/SnippetsContext.tsx
+++ b/client/src/store/SnippetsContext.tsx
@@ -5,14 +5,14 @@ import {
Context,
Snippet,
Response,
- LanguageCount,
+ TagCount,
NewSnippet
} from '../typescript/interfaces';
export const SnippetsContext = createContext({
snippets: [],
currentSnippet: null,
- languageCount: [],
+ tagCount: [],
getSnippets: () => {},
getSnippetById: (id: number) => {},
setSnippet: (id: number) => {},
@@ -20,7 +20,7 @@ export const SnippetsContext = createContext({
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => {},
deleteSnippet: (id: number) => {},
toggleSnippetPin: (id: number) => {},
- countSnippets: () => {}
+ countTags: () => {}
});
interface Props {
@@ -30,7 +30,7 @@ interface Props {
export const SnippetsContextProvider = (props: Props): JSX.Element => {
const [snippets, setSnippets] = useState([]);
const [currentSnippet, setCurrentSnippet] = useState(null);
- const [languageCount, setLanguageCount] = useState([]);
+ const [tagCount, setTagCount] = useState([]);
const history = useHistory();
@@ -132,17 +132,17 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
}
};
- const countSnippets = (): void => {
+ const countTags = (): void => {
axios
- .get>('/api/snippets/statistics/count')
- .then(res => setLanguageCount(res.data.data))
+ .get>('/api/snippets/statistics/count')
+ .then(res => setTagCount(res.data.data))
.catch(err => redirectOnError());
};
const context = {
snippets,
currentSnippet,
- languageCount,
+ tagCount,
getSnippets,
getSnippetById,
setSnippet,
@@ -150,7 +150,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
updateSnippet,
deleteSnippet,
toggleSnippetPin,
- countSnippets
+ countTags
};
return (
diff --git a/client/src/typescript/interfaces/Context.ts b/client/src/typescript/interfaces/Context.ts
index 215674e..6e33734 100644
--- a/client/src/typescript/interfaces/Context.ts
+++ b/client/src/typescript/interfaces/Context.ts
@@ -1,9 +1,9 @@
-import { LanguageCount, NewSnippet, Snippet } from '.';
+import { TagCount, NewSnippet, Snippet } from '.';
export interface Context {
snippets: Snippet[];
currentSnippet: Snippet | null;
- languageCount: LanguageCount[];
+ tagCount: TagCount[];
getSnippets: () => void;
getSnippetById: (id: number) => void;
setSnippet: (id: number) => void;
@@ -11,5 +11,5 @@ export interface Context {
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => void;
deleteSnippet: (id: number) => void;
toggleSnippetPin: (id: number) => void;
- countSnippets: () => void;
+ countTags: () => void;
}
diff --git a/client/src/typescript/interfaces/Snippet.ts b/client/src/typescript/interfaces/Snippet.ts
index 8e1ced5..1973787 100644
--- a/client/src/typescript/interfaces/Snippet.ts
+++ b/client/src/typescript/interfaces/Snippet.ts
@@ -7,7 +7,7 @@ export interface NewSnippet {
code: string;
docs?: string;
isPinned: boolean;
- tags: string;
+ tags: string[];
}
export interface Snippet extends Model, NewSnippet {}
diff --git a/client/src/typescript/interfaces/Statistics.ts b/client/src/typescript/interfaces/Statistics.ts
index 2bf242b..80cedcd 100644
--- a/client/src/typescript/interfaces/Statistics.ts
+++ b/client/src/typescript/interfaces/Statistics.ts
@@ -1,4 +1,4 @@
-export interface LanguageCount {
+export interface TagCount {
count: number;
- language: string;
+ name: string;
}
diff --git a/src/controllers/snippets.ts b/src/controllers/snippets.ts
index fe6a93e..65fac50 100644
--- a/src/controllers/snippets.ts
+++ b/src/controllers/snippets.ts
@@ -2,8 +2,10 @@ import { Request, Response, NextFunction } from 'express';
import { QueryTypes } from 'sequelize';
import { sequelize } from '../db';
import { asyncWrapper } from '../middleware';
-import { SnippetModel } from '../models';
-import { ErrorResponse, tagsParser } from '../utils';
+import { SnippetInstance, SnippetModel } from '../models';
+import { ErrorResponse, getTags, tagsParser, Logger } from '../utils';
+
+const logger = new Logger('snippets-controller');
/**
* @description Create new snippet
@@ -37,7 +39,22 @@ export const createSnippet = asyncWrapper(
*/
export const getAllSnippets = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise => {
- const snippets = await SnippetModel.findAll();
+ const snippets = await SnippetModel.findAll({
+ raw: true
+ });
+
+ await new Promise(async resolve => {
+ try {
+ for await (let snippet of snippets) {
+ const tags = await getTags(+snippet.id);
+ snippet.tags = tags;
+ }
+ } catch (err) {
+ logger.log('Error while fetching tags');
+ } finally {
+ resolve();
+ }
+ });
res.status(200).json({
data: snippets
@@ -46,14 +63,15 @@ export const getAllSnippets = asyncWrapper(
);
/**
- * @description Get single sinppet by id
+ * @description Get single snippet by id
* @route /api/snippets/:id
* @request GET
*/
export const getSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise => {
const snippet = await SnippetModel.findOne({
- where: { id: req.params.id }
+ where: { id: req.params.id },
+ raw: true
});
if (!snippet) {
@@ -65,6 +83,9 @@ export const getSnippet = asyncWrapper(
);
}
+ const tags = await getTags(+req.params.id);
+ snippet.tags = tags;
+
res.status(200).json({
data: snippet
});
@@ -144,19 +165,20 @@ export const deleteSnippet = asyncWrapper(
);
/**
- * @description Count snippets by language
+ * @description Count tags
* @route /api/snippets/statistics/count
* @request GET
*/
-export const countSnippets = asyncWrapper(
+export const countTags = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise => {
const result = await sequelize.query(
`SELECT
- COUNT(language) AS count,
- language
- FROM snippets
- GROUP BY language
- ORDER BY language ASC`,
+ COUNT(tags.name) as count,
+ tags.name
+ FROM snippets_tags
+ INNER JOIN tags ON snippets_tags.tag_id = tags.id
+ GROUP BY tags.name
+ ORDER BY name ASC`,
{
type: QueryTypes.SELECT
}
diff --git a/src/db/migrations/02_tags.ts b/src/db/migrations/02_tags.ts
index d15c3fd..558a144 100644
--- a/src/db/migrations/02_tags.ts
+++ b/src/db/migrations/02_tags.ts
@@ -76,7 +76,6 @@ export const up = async (queryInterface: QueryInterface): Promise => {
}
} catch (err) {
logger.log('Error while assigning tags to snippets');
- console.log(err);
} finally {
resolve();
}
diff --git a/src/routes/snippets.ts b/src/routes/snippets.ts
index 4b3727c..19ca584 100644
--- a/src/routes/snippets.ts
+++ b/src/routes/snippets.ts
@@ -1,6 +1,6 @@
import { Router } from 'express';
import {
- countSnippets,
+ countTags,
createSnippet,
deleteSnippet,
getAllSnippets,
@@ -22,4 +22,4 @@ snippetRouter
.put(updateSnippet)
.delete(deleteSnippet);
-snippetRouter.route('/statistics/count').get(countSnippets);
+snippetRouter.route('/statistics/count').get(countTags);
diff --git a/src/typescript/interfaces/Snippet.ts b/src/typescript/interfaces/Snippet.ts
index 785ee09..2a4c637 100644
--- a/src/typescript/interfaces/Snippet.ts
+++ b/src/typescript/interfaces/Snippet.ts
@@ -8,6 +8,7 @@ export interface Snippet extends Model {
code: string;
docs: string;
isPinned: number;
+ tags?: string[];
}
export interface SnippetCreationAttributes
diff --git a/src/utils/getTags.ts b/src/utils/getTags.ts
new file mode 100644
index 0000000..f44a1c1
--- /dev/null
+++ b/src/utils/getTags.ts
@@ -0,0 +1,18 @@
+import { sequelize } from '../db';
+import { QueryTypes } from 'sequelize';
+
+export const getTags = async (snippetId: number): Promise => {
+ const tags = await sequelize.query<{ name: string }>(
+ `SELECT tags.name
+ FROM tags
+ INNER JOIN
+ snippets_tags ON tags.id = snippets_tags.tag_id
+ INNER JOIN
+ snippets ON snippets.id = snippets_tags.snippet_id
+ WHERE
+ snippets_tags.snippet_id = ${snippetId};`,
+ { type: QueryTypes.SELECT }
+ );
+
+ return tags.map(tag => tag.name);
+};
diff --git a/src/utils/index.ts b/src/utils/index.ts
index c86e0f3..f05b365 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,3 +1,4 @@
export * from './Logger';
export * from './ErrorResponse';
export * from './tagsParser';
+export * from './getTags';