make tags deleteable
seperate sidebar for each different category page
This commit is contained in:
parent
d8aee9e5b7
commit
ac126f6a9d
@ -9,6 +9,7 @@ class Tags extends RequestBase {
|
|||||||
function initHandlers() {
|
function initHandlers() {
|
||||||
$this->addToDB();
|
$this->addToDB();
|
||||||
$this->getFromDB();
|
$this->getFromDB();
|
||||||
|
$this->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addToDB() {
|
private function addToDB() {
|
||||||
@ -65,4 +66,36 @@ class Tags extends RequestBase {
|
|||||||
$this->commitMessage(json_encode($rows));
|
$this->commitMessage(json_encode($rows));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function delete() {
|
||||||
|
/**
|
||||||
|
* delete a Tag with specified id
|
||||||
|
*/
|
||||||
|
$this->addActionHandler("deleteTag", function () {
|
||||||
|
$tag_id = $_POST['tagId'];
|
||||||
|
$force = $_POST['force'];
|
||||||
|
|
||||||
|
// delete key constraints first
|
||||||
|
if ($force === "true") {
|
||||||
|
$query = "DELETE FROM video_tags WHERE tag_id=$tag_id";
|
||||||
|
|
||||||
|
if ($this->conn->query($query) !== TRUE) {
|
||||||
|
$this->commitMessage('{"result":"' . $this->conn->error . '"}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = "DELETE FROM tags WHERE tag_id=$tag_id";
|
||||||
|
|
||||||
|
if ($this->conn->query($query) === TRUE) {
|
||||||
|
$this->commitMessage('{"result":"success"}');
|
||||||
|
} else {
|
||||||
|
// check if error is a constraint error
|
||||||
|
if (preg_match('/^.*a foreign key constraint fails.*$/i', $this->conn->error)) {
|
||||||
|
$this->commitMessage('{"result":"not empty tag"}');
|
||||||
|
} else {
|
||||||
|
$this->commitMessage('{"result":"' . $this->conn->eror . '"}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import GlobalInfos from '../../utils/GlobalInfos';
|
|||||||
|
|
||||||
interface props {
|
interface props {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string | null;
|
subtitle: string | number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,60 +22,14 @@ describe('<AddTagPopup/>', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('test tag click', function () {
|
it('test tag click', function () {
|
||||||
const wrapper = shallow(<AddTagPopup/>);
|
const wrapper = shallow(<AddTagPopup submit={jest.fn()} onHide={jest.fn()}/>);
|
||||||
wrapper.instance().addTag = jest.fn();
|
|
||||||
|
|
||||||
wrapper.setState({
|
wrapper.setState({
|
||||||
items: [{tag_id: 1, tag_name: 'test'}]
|
items: [{tag_id: 1, tag_name: 'test'}]
|
||||||
}, () => {
|
}, () => {
|
||||||
wrapper.find('Tag').first().dive().simulate('click');
|
wrapper.find('Tag').first().dive().simulate('click');
|
||||||
expect(wrapper.instance().addTag).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test addtag', done => {
|
|
||||||
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
|
|
||||||
|
|
||||||
global.fetch = prepareFetchApi({result: 'success'});
|
|
||||||
|
|
||||||
wrapper.setProps({
|
|
||||||
submit: jest.fn(() => {}),
|
|
||||||
onHide: jest.fn()
|
|
||||||
}, () => {
|
|
||||||
wrapper.instance().addTag(1, 'test');
|
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.nextTick(() => {
|
|
||||||
expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(1);
|
expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(1);
|
||||||
expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
|
expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
global.fetch.mockClear();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test failing addTag', done => {
|
|
||||||
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
|
|
||||||
|
|
||||||
global.fetch = prepareFetchApi({result: 'fail'});
|
|
||||||
|
|
||||||
wrapper.setProps({
|
|
||||||
submit: jest.fn(() => {}),
|
|
||||||
onHide: jest.fn()
|
|
||||||
}, () => {
|
|
||||||
wrapper.instance().addTag(1, 'test');
|
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.nextTick(() => {
|
|
||||||
expect(wrapper.instance().props.submit).toHaveBeenCalledTimes(0);
|
|
||||||
expect(wrapper.instance().props.onHide).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
global.fetch.mockClear();
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,6 @@ import Tag from '../../Tag/Tag';
|
|||||||
import PopupBase from '../PopupBase';
|
import PopupBase from '../PopupBase';
|
||||||
import {callAPI} from '../../../utils/Api';
|
import {callAPI} from '../../../utils/Api';
|
||||||
import {TagType} from '../../../types/VideoTypes';
|
import {TagType} from '../../../types/VideoTypes';
|
||||||
import {GeneralSuccess} from '../../../types/GeneralTypes';
|
|
||||||
|
|
||||||
interface props {
|
interface props {
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
@ -41,29 +40,13 @@ class AddTagPopup extends React.Component<props, state> {
|
|||||||
this.state.items.map((i) => (
|
this.state.items.map((i) => (
|
||||||
<Tag tagInfo={i}
|
<Tag tagInfo={i}
|
||||||
onclick={(): void => {
|
onclick={(): void => {
|
||||||
this.addTag(i.tag_id, i.tag_name);
|
this.props.submit(i.tag_id, i.tag_name);
|
||||||
|
this.props.onHide();
|
||||||
}}/>
|
}}/>
|
||||||
)) : null}
|
)) : null}
|
||||||
</PopupBase>
|
</PopupBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* add a new tag to this video
|
|
||||||
* @param tagid tag id to add
|
|
||||||
* @param tagname tag name to add
|
|
||||||
*/
|
|
||||||
addTag(tagid: number, tagname: string): void {
|
|
||||||
callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, (result: GeneralSuccess) => {
|
|
||||||
if (result.result !== 'success') {
|
|
||||||
console.log('error occured while writing to db -- todo error handling');
|
|
||||||
console.log(result.result);
|
|
||||||
} else {
|
|
||||||
this.props.submit(tagid, tagname);
|
|
||||||
}
|
|
||||||
this.props.onHide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AddTagPopup;
|
export default AddTagPopup;
|
||||||
|
28
src/elements/Popups/SubmitPopup/SubmitPopup.test.js
Normal file
28
src/elements/Popups/SubmitPopup/SubmitPopup.test.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {shallow} from "enzyme";
|
||||||
|
import React from "react";
|
||||||
|
import SubmitPopup from "./SubmitPopup";
|
||||||
|
|
||||||
|
describe('<SubmitPopup/>', function () {
|
||||||
|
it('renders without crashing ', function () {
|
||||||
|
const wrapper = shallow(<SubmitPopup/>);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test submit click', function () {
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<SubmitPopup submit={() => func()}/>);
|
||||||
|
|
||||||
|
wrapper.find('Button').findWhere(p => p.props().title === 'Submit').simulate('click');
|
||||||
|
|
||||||
|
expect(func).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test cancel click', function () {
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<SubmitPopup onHide={() => func()}/>);
|
||||||
|
|
||||||
|
wrapper.find('Button').findWhere(p => p.props().title === 'Cancel').simulate('click');
|
||||||
|
|
||||||
|
expect(func).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
18
src/elements/Popups/SubmitPopup/SubmitPopup.tsx
Normal file
18
src/elements/Popups/SubmitPopup/SubmitPopup.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PopupBase from '../PopupBase';
|
||||||
|
import {Button} from '../../GPElements/Button';
|
||||||
|
|
||||||
|
interface props {
|
||||||
|
onHide: (_: void) => void;
|
||||||
|
submit: (_: void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SubmitPopup(props: props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<PopupBase title='Are you sure?' onHide={props.onHide} height='160px' width='300px'>
|
||||||
|
<Button title='Submit' color={{backgroundColor: 'green'}} onClick={(): void => props.submit()}/>
|
||||||
|
<Button title='Cancel' color={{backgroundColor: 'red'}} onClick={(): void => props.onHide()}/>
|
||||||
|
</PopupBase>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -7,41 +7,4 @@ describe('<CategoryPage/>', function () {
|
|||||||
const wrapper = shallow(<CategoryPage/>);
|
const wrapper = shallow(<CategoryPage/>);
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test new tag popup', function () {
|
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
|
||||||
|
|
||||||
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
|
||||||
wrapper.find('[data-testid="btnaddtag"]').simulate('click');
|
|
||||||
// newtagpopup should be showing now
|
|
||||||
expect(wrapper.find('NewTagPopup')).toHaveLength(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test add popup', function () {
|
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
|
||||||
|
|
||||||
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
|
||||||
wrapper.setState({popupvisible: true});
|
|
||||||
expect(wrapper.find('NewTagPopup')).toHaveLength(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test hiding of popup', function () {
|
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
|
||||||
wrapper.setState({popupvisible: true});
|
|
||||||
|
|
||||||
wrapper.find('NewTagPopup').props().onHide();
|
|
||||||
|
|
||||||
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test setting of subtitle', function () {
|
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
|
||||||
|
|
||||||
expect(wrapper.find('PageTitle').props().subtitle).not.toBe('testtitle');
|
|
||||||
|
|
||||||
wrapper.instance().setSubTitle('testtitle');
|
|
||||||
|
|
||||||
// test if prop of title is set correctly
|
|
||||||
expect(wrapper.find('PageTitle').props().subtitle).toBe('testtitle');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,82 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
|
|
||||||
import Tag from '../../elements/Tag/Tag';
|
|
||||||
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
|
|
||||||
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
|
||||||
import {Route, Switch} from 'react-router-dom';
|
import {Route, Switch} from 'react-router-dom';
|
||||||
import {DefaultTags} from '../../types/GeneralTypes';
|
|
||||||
import {CategoryViewWR} from './CategoryView';
|
import {CategoryViewWR} from './CategoryView';
|
||||||
import TagView from './TagView';
|
import TagView from './TagView';
|
||||||
|
|
||||||
|
|
||||||
interface CategoryPageState {
|
|
||||||
popupvisible: boolean;
|
|
||||||
subtitle: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for Category Page
|
* Component for Category Page
|
||||||
* Contains a Tag Overview and loads specific Tag videos in VideoContainer
|
* Contains a Tag Overview and loads specific Tag videos in VideoContainer
|
||||||
*/
|
*/
|
||||||
class CategoryPage extends React.Component<{}, CategoryPageState> {
|
class CategoryPage extends React.Component {
|
||||||
constructor(props: {}) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
popupvisible: false,
|
|
||||||
subtitle: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setSubTitle = this.setSubTitle.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<Switch>
|
||||||
<PageTitle
|
<Route path='/categories/:id'>
|
||||||
title='Categories'
|
<CategoryViewWR/>
|
||||||
subtitle={this.state.subtitle}/>
|
</Route>
|
||||||
|
<Route path='/categories'>
|
||||||
<SideBar>
|
<TagView/>
|
||||||
<SideBarTitle>Default Tags:</SideBarTitle>
|
</Route>
|
||||||
<Tag tagInfo={DefaultTags.all}/>
|
</Switch>
|
||||||
<Tag tagInfo={DefaultTags.fullhd}/>
|
|
||||||
<Tag tagInfo={DefaultTags.hd}/>
|
|
||||||
<Tag tagInfo={DefaultTags.lowq}/>
|
|
||||||
|
|
||||||
<Line/>
|
|
||||||
<button data-testid='btnaddtag' className='btn btn-success' onClick={(): void => {
|
|
||||||
this.setState({popupvisible: true});
|
|
||||||
}}>Add a new Tag!
|
|
||||||
</button>
|
|
||||||
</SideBar>
|
|
||||||
<Switch>
|
|
||||||
<Route path='/categories/:id'>
|
|
||||||
<CategoryViewWR setSubTitle={this.setSubTitle}/>
|
|
||||||
</Route>
|
|
||||||
<Route path='/categories'>
|
|
||||||
<TagView setSubTitle={this.setSubTitle}/>
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
{this.state.popupvisible ?
|
|
||||||
<NewTagPopup onHide={(): void => {
|
|
||||||
this.setState({popupvisible: false});
|
|
||||||
// this.loadTags();
|
|
||||||
}}/> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* set the subtitle of this page
|
|
||||||
* @param subtitle string as subtitle
|
|
||||||
*/
|
|
||||||
setSubTitle(subtitle: string): void {
|
|
||||||
this.setState({subtitle: subtitle});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CategoryPage;
|
export default CategoryPage;
|
||||||
|
@ -4,7 +4,7 @@ import {CategoryView} from './CategoryView';
|
|||||||
|
|
||||||
describe('<CategoryView/>', function () {
|
describe('<CategoryView/>', function () {
|
||||||
function instance() {
|
function instance() {
|
||||||
return shallow(<CategoryView match={{params: {id: 10}}}/>);
|
return shallow(<CategoryView match={{params: {id: 10}}} history={{push: jest.fn()}}/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -21,4 +21,54 @@ describe('<CategoryView/>', function () {
|
|||||||
wrapper.find('button').simulate('click');
|
wrapper.find('button').simulate('click');
|
||||||
expect(func).toHaveBeenCalledTimes(1);
|
expect(func).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test delete of tag', function () {
|
||||||
|
const wrapper = instance();
|
||||||
|
callAPIMock({result: 'success'});
|
||||||
|
|
||||||
|
// simulate button click
|
||||||
|
wrapper.find('Button').props().onClick();
|
||||||
|
|
||||||
|
expect(wrapper.instance().props.history.push).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test delete of non empty tag', function () {
|
||||||
|
const wrapper = instance();
|
||||||
|
callAPIMock({result: 'not empty tag'});
|
||||||
|
|
||||||
|
// simulate button click
|
||||||
|
wrapper.find('Button').props().onClick();
|
||||||
|
|
||||||
|
// expect SubmitPopup showing
|
||||||
|
expect(wrapper.find('SubmitPopup')).toHaveLength(1);
|
||||||
|
|
||||||
|
// mock deleteTag function
|
||||||
|
wrapper.instance().deleteTag = jest.fn((v) => {});
|
||||||
|
|
||||||
|
// simulate submit
|
||||||
|
wrapper.find('SubmitPopup').props().submit();
|
||||||
|
|
||||||
|
// expect deleteTag function to have been called with force parameter
|
||||||
|
expect(wrapper.instance().deleteTag).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test cancel of ', function () {
|
||||||
|
const wrapper = instance();
|
||||||
|
callAPIMock({result: 'not empty tag'});
|
||||||
|
|
||||||
|
// simulate button click
|
||||||
|
wrapper.find('Button').props().onClick();
|
||||||
|
|
||||||
|
// expect SubmitPopup showing
|
||||||
|
expect(wrapper.find('SubmitPopup')).toHaveLength(1);
|
||||||
|
|
||||||
|
// mock deleteTag function
|
||||||
|
wrapper.instance().deleteTag = jest.fn((v) => {});
|
||||||
|
|
||||||
|
// simulate submit
|
||||||
|
wrapper.find('SubmitPopup').props().onHide();
|
||||||
|
|
||||||
|
// expect deleteTag function to have been called with force parameter
|
||||||
|
expect(wrapper.instance().deleteTag).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,13 +4,18 @@ import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
|||||||
import {callAPI} from '../../utils/Api';
|
import {callAPI} from '../../utils/Api';
|
||||||
import {withRouter} from 'react-router-dom';
|
import {withRouter} from 'react-router-dom';
|
||||||
import {VideoTypes} from '../../types/ApiTypes';
|
import {VideoTypes} from '../../types/ApiTypes';
|
||||||
|
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
||||||
|
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
|
||||||
|
import Tag from '../../elements/Tag/Tag';
|
||||||
|
import {DefaultTags, GeneralSuccess} from '../../types/GeneralTypes';
|
||||||
|
import {Button} from '../../elements/GPElements/Button';
|
||||||
|
import SubmitPopup from '../../elements/Popups/SubmitPopup/SubmitPopup';
|
||||||
|
|
||||||
interface CategoryViewProps extends RouteComponentProps<{ id: string }> {
|
interface CategoryViewProps extends RouteComponentProps<{ id: string }> {}
|
||||||
setSubTitle: (title: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CategoryViewState {
|
interface CategoryViewState {
|
||||||
loaded: boolean
|
loaded: boolean;
|
||||||
|
submitForceDelete: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,7 +28,8 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loaded: false
|
loaded: false,
|
||||||
|
submitForceDelete: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +48,20 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
|
|||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageTitle
|
||||||
|
title='Categories'
|
||||||
|
subtitle={this.videodata.length + ' Videos'}/>
|
||||||
|
|
||||||
|
<SideBar>
|
||||||
|
<SideBarTitle>Default Tags:</SideBarTitle>
|
||||||
|
<Tag tagInfo={DefaultTags.all}/>
|
||||||
|
<Tag tagInfo={DefaultTags.fullhd}/>
|
||||||
|
<Tag tagInfo={DefaultTags.hd}/>
|
||||||
|
<Tag tagInfo={DefaultTags.lowq}/>
|
||||||
|
|
||||||
|
<Line/>
|
||||||
|
<Button title='Delete Tag' onClick={(): void => {this.deleteTag(false);}} color={{backgroundColor: 'red'}}/>
|
||||||
|
</SideBar>
|
||||||
{this.state.loaded ?
|
{this.state.loaded ?
|
||||||
<VideoContainer
|
<VideoContainer
|
||||||
data={this.videodata}/> : null}
|
data={this.videodata}/> : null}
|
||||||
@ -51,22 +71,50 @@ export class CategoryView extends React.Component<CategoryViewProps, CategoryVie
|
|||||||
this.props.history.push('/categories');
|
this.props.history.push('/categories');
|
||||||
}}>Back to Categories
|
}}>Back to Categories
|
||||||
</button>
|
</button>
|
||||||
|
{this.handlePopups()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handlePopups(): JSX.Element {
|
||||||
|
if (this.state.submitForceDelete) {
|
||||||
|
return (<SubmitPopup
|
||||||
|
onHide={(): void => this.setState({submitForceDelete: false})}
|
||||||
|
submit={(): void => {this.deleteTag(true);}}/>);
|
||||||
|
} else {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetch data for a specific tag from backend
|
* fetch data for a specific tag from backend
|
||||||
* @param id tagid
|
* @param id tagid
|
||||||
*/
|
*/
|
||||||
fetchVideoData(id: number): void {
|
private fetchVideoData(id: number): void {
|
||||||
callAPI<VideoTypes.VideoUnloadedType[]>('video.php', {action: 'getMovies', tag: id}, result => {
|
callAPI<VideoTypes.VideoUnloadedType[]>('video.php', {action: 'getMovies', tag: id}, result => {
|
||||||
this.videodata = result;
|
this.videodata = result;
|
||||||
this.setState({loaded: true});
|
this.setState({loaded: true});
|
||||||
this.props.setSubTitle(this.videodata.length + ' Videos');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete the current tag
|
||||||
|
*/
|
||||||
|
private deleteTag(force: boolean): void {
|
||||||
|
callAPI<GeneralSuccess>('tags.php', {
|
||||||
|
action: 'deleteTag',
|
||||||
|
tagId: parseInt(this.props.match.params.id),
|
||||||
|
force: force
|
||||||
|
}, result => {
|
||||||
|
console.log(result.result);
|
||||||
|
if (result.result === 'success') {
|
||||||
|
this.props.history.push('/categories');
|
||||||
|
} else if (result.result === 'not empty tag') {
|
||||||
|
// show submisison tag to ask if really delete
|
||||||
|
this.setState({submitForceDelete: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,4 +14,30 @@ describe('<TagView/>', function () {
|
|||||||
|
|
||||||
expect(wrapper.find('TagPreview')).toHaveLength(1);
|
expect(wrapper.find('TagPreview')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test new tag popup', function () {
|
||||||
|
const wrapper = shallow(<TagView/>);
|
||||||
|
|
||||||
|
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
||||||
|
wrapper.find('[data-testid="btnaddtag"]').simulate('click');
|
||||||
|
// newtagpopup should be showing now
|
||||||
|
expect(wrapper.find('NewTagPopup')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test add popup', function () {
|
||||||
|
const wrapper = shallow(<TagView/>);
|
||||||
|
|
||||||
|
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
||||||
|
wrapper.setState({popupvisible: true});
|
||||||
|
expect(wrapper.find('NewTagPopup')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test hiding of popup', function () {
|
||||||
|
const wrapper = shallow(<TagView/>);
|
||||||
|
wrapper.setState({popupvisible: true});
|
||||||
|
|
||||||
|
wrapper.find('NewTagPopup').props().onHide();
|
||||||
|
|
||||||
|
expect(wrapper.find('NewTagPopup')).toHaveLength(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,20 +4,27 @@ import videocontainerstyle from '../../elements/VideoContainer/VideoContainer.mo
|
|||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import {TagPreview} from '../../elements/Preview/Preview';
|
import {TagPreview} from '../../elements/Preview/Preview';
|
||||||
import {callAPI} from '../../utils/Api';
|
import {callAPI} from '../../utils/Api';
|
||||||
|
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
||||||
|
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
|
||||||
|
import Tag from '../../elements/Tag/Tag';
|
||||||
|
import {DefaultTags} from '../../types/GeneralTypes';
|
||||||
|
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
|
||||||
|
|
||||||
interface TagViewState {
|
interface TagViewState {
|
||||||
loadedtags: TagType[];
|
loadedtags: TagType[];
|
||||||
|
popupvisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface props {
|
interface props {}
|
||||||
setSubTitle: (title: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
class TagView extends React.Component<props, TagViewState> {
|
class TagView extends React.Component<props, TagViewState> {
|
||||||
constructor(props: props) {
|
constructor(props: props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {loadedtags: []};
|
this.state = {
|
||||||
|
loadedtags: [],
|
||||||
|
popupvisible: false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@ -27,6 +34,23 @@ class TagView extends React.Component<props, TagViewState> {
|
|||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageTitle
|
||||||
|
title='Categories'
|
||||||
|
subtitle={this.state.loadedtags.length + ' different Tags'}/>
|
||||||
|
|
||||||
|
<SideBar>
|
||||||
|
<SideBarTitle>Default Tags:</SideBarTitle>
|
||||||
|
<Tag tagInfo={DefaultTags.all}/>
|
||||||
|
<Tag tagInfo={DefaultTags.fullhd}/>
|
||||||
|
<Tag tagInfo={DefaultTags.hd}/>
|
||||||
|
<Tag tagInfo={DefaultTags.lowq}/>
|
||||||
|
|
||||||
|
<Line/>
|
||||||
|
<button data-testid='btnaddtag' className='btn btn-success' onClick={(): void => {
|
||||||
|
this.setState({popupvisible: true});
|
||||||
|
}}>Add a new Tag!
|
||||||
|
</button>
|
||||||
|
</SideBar>
|
||||||
<div className={videocontainerstyle.maincontent}>
|
<div className={videocontainerstyle.maincontent}>
|
||||||
{this.state.loadedtags ?
|
{this.state.loadedtags ?
|
||||||
this.state.loadedtags.map((m) => (
|
this.state.loadedtags.map((m) => (
|
||||||
@ -36,6 +60,7 @@ class TagView extends React.Component<props, TagViewState> {
|
|||||||
)) :
|
)) :
|
||||||
'loading'}
|
'loading'}
|
||||||
</div>
|
</div>
|
||||||
|
{this.handlePopups()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,9 +71,21 @@ class TagView extends React.Component<props, TagViewState> {
|
|||||||
loadTags(): void {
|
loadTags(): void {
|
||||||
callAPI<TagType[]>('tags.php', {action: 'getAllTags'}, result => {
|
callAPI<TagType[]>('tags.php', {action: 'getAllTags'}, result => {
|
||||||
this.setState({loadedtags: result});
|
this.setState({loadedtags: result});
|
||||||
this.props.setSubTitle(result.length + ' different Tags');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handlePopups(): JSX.Element {
|
||||||
|
if (this.state.popupvisible) {
|
||||||
|
return (
|
||||||
|
<NewTagPopup onHide={(): void => {
|
||||||
|
this.setState({popupvisible: false});
|
||||||
|
this.loadTags();
|
||||||
|
}}/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (<></>);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TagView;
|
export default TagView;
|
||||||
|
Loading…
Reference in New Issue
Block a user