Skip to content

Commit

Permalink
feat: integrate tags in admin panel (#405)
Browse files Browse the repository at this point in the history
* fix(admin): tag styling

* feat: integrate tags in admin panel

* feat: add tag autocomplete

* fix: update tags
  • Loading branch information
Shurtu-gal authored Feb 5, 2023
1 parent 99989c5 commit 5476c71
Show file tree
Hide file tree
Showing 11 changed files with 326 additions and 93 deletions.
7 changes: 3 additions & 4 deletions client/src/components/admin_v2/Common/FeaturedMedia.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import React, { useEffect, useState } from 'react';
// libraries
import { Card, Typography } from '@mui/material';

// media imports
import SharedMedia, { thumb, thumbInner } from './sharedMedia';
import imagekit from '../../../utils/imagekit';

// graphql
import { GraphClient } from '../../../config/ApolloClient';
import addMedia from '../../../graphql/mutations/media/addMedia';
import imagekit from '../../../utils/imageKit';
// media imports
import SharedMedia, { thumb, thumbInner } from './sharedMedia';

export default function FeaturedMedia({
height,
Expand Down
202 changes: 161 additions & 41 deletions client/src/components/admin_v2/Common/Tags.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Cancel } from '@mui/icons-material';
import { useEffect, useRef, useState } from 'react';

import { Cancel, Sell } from '@mui/icons-material';
import { Stack, TextField, Typography } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/system';
import { useEffect } from 'react';
import { useRef, useState } from 'react';

import { GraphClient } from '../../../config/ApolloClient';
import updateArticleTags from '../../../graphql/mutations/article/updateArticleTags';
import createTag from '../../../graphql/mutations/tag/createTag';
import useTagAutoComplete from '../../../hooks/useTagAutoComplete';

const Tags = ({ data, handleDelete }) => {
return (
<Box
sx={{
background: '#2196f3',
background: data.isAdmin ? '#ff0000' : '#2196f3',
height: '100%',
display: 'flex',
padding: '0.4rem',
Expand All @@ -23,9 +29,10 @@ const Tags = ({ data, handleDelete }) => {
<Typography
sx={{
fontSize: '.6rem',
whiteSpace: 'nowrap',
}}
>
{data}
{data.name}
</Typography>
<Cancel
sx={{ cursor: 'pointer', fontSize: '.6rem' }}
Expand All @@ -38,54 +45,167 @@ const Tags = ({ data, handleDelete }) => {
);
};

export default function ArticleTags({ tag, underlineVisible = false }) {
const [tags, SetTags] = useState([]);
export default function ArticleTags({
tag,
adminTags,
isAdmin,
id,
setErrorMessageAndError,
}) {
const [tags, setTags] = useState([]);
const [active, setActive] = useState(false);
const [search, setSearch] = useState('');
const tagRef = useRef();
const classes = useStyles({ active });

const autoCompleteData = useTagAutoComplete(search, isAdmin, 5);

useEffect(() => {
console.log(tag);
if (tag) {
const tagData = tag.map((data) => data.name);
SetTags(tagData);
if (adminTags) {
const adminTagData = adminTags.map((data) => ({
...data,
isAdmin: true,
}));
setTags([...adminTagData, ...tag]);
}
}, [tag]);
}, []);

const handleDelete = (value) => {
const newtags = tags.filter((val) => val !== value);
SetTags(newtags);
const handleDelete = async (value) => {
await updateTag(value.reference, false);
};
const handleOnSubmit = (e) => {
e.preventDefault();

if (tagRef.current.value === '') return;
const handleCreate = async () => {
const {
data: { createTag: createdTag },
} = await GraphClient.mutate({
mutation: createTag,
variables: {
name: tagRef.current.value,
isAdmin: isAdmin,
},
});

await updateTag(createdTag.id, true);
};

const updateTag = async (tagId, isAdded) => {
if (tags.some((tag) => tag.reference === tagId && isAdded)) {
setErrorMessageAndError
? setErrorMessageAndError('Tag already exists')
: null;
tagRef.current.value = '';
setActive(false);
return false;
}

const {
data: { updateArticleTags: updatedTags },
} = await GraphClient.mutate({
mutation: updateArticleTags,
variables: {
id: id,
tag: tagId,
isAdded: isAdded,
isAdmin: isAdmin,
},
});

SetTags([...tags, tagRef.current.value]);
const updatedAdminTags = isAdmin
? updatedTags.adminTags.map((data) => ({
...data,
isAdmin: true,
}))
: [];
const updatedTag = isAdmin ? [] : updatedTags.tags;
setTags([...updatedAdminTags, ...updatedTag]);
tagRef.current.value = '';
setActive(false);
};

const searchQuery = (e) => {
e.preventDefault();
setSearch(e.target.value);
};

const searchActive = () => {
setActive((current) => !current);
};

return (
<Box sx={{ flexGrow: 1 }}>
<form onSubmit={handleOnSubmit}>
<TextField
inputRef={tagRef}
fullWidth
variant='standard'
size='small'
margin='none'
placeholder={tags.length < 2 ? 'tags' : ''}
InputProps={{
startAdornment: (
<Box sx={{ margin: '0 0.2rem 0 0', display: 'flex' }}>
{tags.map((data, index) => {
return (
<Tags data={data} handleDelete={handleDelete} key={index} />
);
})}
</Box>
),
disableUnderline: underlineVisible,
}}
/>
</form>
<div className={classes.tagForm}>
<Box sx={{ margin: '0 0.2rem 0 0', display: 'flex' }}>
{tags.map((data, index) => {
return <Tags data={data} handleDelete={handleDelete} key={index} />;
})}
</Box>
<Sell onClick={searchActive} sx={{ cursor: 'pointer' }} />
<div className={classes.search}>
<TextField
inputRef={tagRef}
onChange={searchQuery}
fullWidth
variant='outlined'
margin='none'
placeholder={'tags'}
inputProps={{
style: { height: '0px' },
}}
/>
{tagRef.current?.value?.length ? (
<div className={classes.tagSuggestions}>
{autoCompleteData?.map(({ id, name }) => (
<div
key={id}
className={classes.tagList}
onClick={() => updateTag(id, true)}
>
{name}
</div>
))}
<div className={classes.tagList} onClick={handleCreate}>
Create Tag: {tagRef.current.value}
</div>
</div>
) : (
<></>
)}
</div>
</div>
</Box>
);
}

const useStyles = makeStyles({
search: {
width: (_) => (_.active ? 150 : 0),
opacity: (_) => (_.active ? 1 : 0),
transition: '1s',
top: '0px',
position: 'relative',
},
tagForm: {
display: 'flex',
alignItems: 'center',
gap: 10,
},
tagSuggestions: {
position: 'absolute',
background: '#FEFEFF',
width: '100%',
padding: '10px',
zIndex: '20022',
borderRadius: '0px 0px 5px 5px',
border: '1px #ECEDEC',
borderStyle: 'none solid solid',
boxShadow: '0px 0px 5px grey',
display: (_) => (_.active ? 'block' : 'none'),
},
tagList: {
position: 'absoulte',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
fontSize: '14px',
},
});
24 changes: 24 additions & 0 deletions client/src/graphql/mutations/article/updateArticleTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { gql } from '@apollo/client';

const updateArticleTags = gql`
mutation ($id: ID!, $tag: ID!, $isAdded: Boolean!, $isAdmin: Boolean!) {
updateArticleTags(
id: $id
tag: $tag
isAdded: $isAdded
isAdmin: $isAdmin
) {
tags {
name
reference
isAdmin
}
adminTags {
name
reference
}
}
}
`;

export default updateArticleTags;
13 changes: 13 additions & 0 deletions client/src/graphql/mutations/tag/createTag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { gql } from '@apollo/client';

const createTag = gql`
mutation createTag($name: String!, $isAdmin: Boolean, $adminColor: String) {
createTag(name: $name, isAdmin: $isAdmin, adminColor: $adminColor) {
id
name
isAdmin
}
}
`;

export default createTag;
5 changes: 5 additions & 0 deletions client/src/graphql/queries/article/listAllArticles.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const listAllArticles = gql`
categories {
number
}
tags {
name
reference
isAdmin
}
adminTags {
name
reference
Expand Down
16 changes: 16 additions & 0 deletions client/src/graphql/queries/tag/getTagAutocomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { gql } from '@apollo/client';

const getAutocomplete = gql`
query ($searchTerm: String!, $isAdmin: Boolean!, $limit: Int) {
getTagAutocomplete(
searchTerm: $searchTerm
isAdmin: $isAdmin
limit: $limit
) {
id
name
isAdmin
}
}
`;
export default getAutocomplete;
40 changes: 40 additions & 0 deletions client/src/hooks/useTagAutoComplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useEffect, useState } from 'react';

import { GraphClient } from '../config/ApolloClient';
import getTagAutocomplete from '../graphql/queries/tag/getTagAutocomplete';

const useTagAutoComplete = (searchTag, isAdmin, limit) => {
const [searchKeyword, setSearchKeyword] = useState(searchTag);
const [result, setResult] = useState([]);

useEffect(() => {
const timer = setTimeout(() => {
setSearchKeyword(searchTag);
}, 100);

return () => clearTimeout(timer);
}, [searchTag]);

useEffect(() => {
if (!searchKeyword) return;

(async () => {
const {
data: { getTagAutocomplete: autoCompleteResult },
} = await GraphClient.query({
query: getTagAutocomplete,
variables: {
searchTerm: searchKeyword,
isAdmin: isAdmin,
limit: limit,
},
});

setResult(autoCompleteResult);
})();
}, [searchKeyword]);

return result;
};

export default useTagAutoComplete;
Loading

0 comments on commit 5476c71

Please sign in to comment.