mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-24 06:28:07 +01:00
Include tags with snippets. Filter by tags. Display tags on single snippet view.
This commit is contained in:
@@ -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 => {
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
{/* TAGS */}
|
||||
<div>
|
||||
{tags.map(tag => (
|
||||
<span className='me-2'>
|
||||
<Badge text={tag} color='dark' />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
{/* ACTIONS */}
|
||||
<div className='d-grid g-2' style={{ rowGap: '10px' }}>
|
||||
<Button
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, Card, EmptyState, Layout } from '../components/UI';
|
||||
import { Snippet } from '../typescript/interfaces';
|
||||
|
||||
export const Snippets = (): JSX.Element => {
|
||||
const { snippets, languageCount, getSnippets, countSnippets } =
|
||||
const { snippets, tagCount, getSnippets, countTags } =
|
||||
useContext(SnippetsContext);
|
||||
|
||||
const [filter, setFilter] = useState<string | null>(null);
|
||||
@@ -13,16 +13,16 @@ export const Snippets = (): JSX.Element => {
|
||||
|
||||
useEffect(() => {
|
||||
getSnippets();
|
||||
countSnippets();
|
||||
countTags();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalSnippets([...snippets]);
|
||||
}, [snippets]);
|
||||
|
||||
const filterHandler = (language: string) => {
|
||||
setFilter(language);
|
||||
const filteredSnippets = snippets.filter(s => s.language === language);
|
||||
const filterHandler = (tag: string) => {
|
||||
setFilter(tag);
|
||||
const filteredSnippets = snippets.filter(s => s.tags.includes(tag));
|
||||
setLocalSnippets(filteredSnippets);
|
||||
};
|
||||
|
||||
@@ -44,21 +44,21 @@ export const Snippets = (): JSX.Element => {
|
||||
<span>Total</span>
|
||||
<span>{snippets.length}</span>
|
||||
</div>
|
||||
<h5 className='card-title'>Filter by language</h5>
|
||||
<h5 className='card-title'>Filter by tags</h5>
|
||||
<Fragment>
|
||||
{languageCount.map((el, idx) => {
|
||||
const isActiveFilter = filter === el.language;
|
||||
{tagCount.map((tag, idx) => {
|
||||
const isActiveFilter = filter === tag.name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={idx}
|
||||
className={`d-flex justify-content-between cursor-pointer ${
|
||||
isActiveFilter && 'text-dark fw-bold'
|
||||
}`}
|
||||
key={idx}
|
||||
onClick={() => filterHandler(el.language)}
|
||||
onClick={() => filterHandler(tag.name)}
|
||||
>
|
||||
<span>{el.language}</span>
|
||||
<span>{el.count}</span>
|
||||
<span>{tag.name}</span>
|
||||
<span>{tag.count}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -5,14 +5,14 @@ import {
|
||||
Context,
|
||||
Snippet,
|
||||
Response,
|
||||
LanguageCount,
|
||||
TagCount,
|
||||
NewSnippet
|
||||
} from '../typescript/interfaces';
|
||||
|
||||
export const SnippetsContext = createContext<Context>({
|
||||
snippets: [],
|
||||
currentSnippet: null,
|
||||
languageCount: [],
|
||||
tagCount: [],
|
||||
getSnippets: () => {},
|
||||
getSnippetById: (id: number) => {},
|
||||
setSnippet: (id: number) => {},
|
||||
@@ -20,7 +20,7 @@ export const SnippetsContext = createContext<Context>({
|
||||
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<Snippet[]>([]);
|
||||
const [currentSnippet, setCurrentSnippet] = useState<Snippet | null>(null);
|
||||
const [languageCount, setLanguageCount] = useState<LanguageCount[]>([]);
|
||||
const [tagCount, setTagCount] = useState<TagCount[]>([]);
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
@@ -132,17 +132,17 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
|
||||
}
|
||||
};
|
||||
|
||||
const countSnippets = (): void => {
|
||||
const countTags = (): void => {
|
||||
axios
|
||||
.get<Response<LanguageCount[]>>('/api/snippets/statistics/count')
|
||||
.then(res => setLanguageCount(res.data.data))
|
||||
.get<Response<TagCount[]>>('/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 (
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface NewSnippet {
|
||||
code: string;
|
||||
docs?: string;
|
||||
isPinned: boolean;
|
||||
tags: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export interface Snippet extends Model, NewSnippet {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface LanguageCount {
|
||||
export interface TagCount {
|
||||
count: number;
|
||||
language: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user