Skip to content

Commit 7f9e197

Browse files
committed
filters and search functionality
1 parent a830a9c commit 7f9e197

File tree

9 files changed

+373
-233
lines changed

9 files changed

+373
-233
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { Component } from "react";
2+
import "./Filter.scss";
3+
import Form from "react-bootstrap/Form";
4+
import Button from "react-bootstrap/Button";
5+
import Dropdown from "react-bootstrap/Dropdown";
6+
import CheckOutlinedIcon from "@material-ui/icons/CheckOutlined";
7+
import SearchOutlinedIcon from "@material-ui/icons/SearchOutlined";
8+
9+
// Author, tags and status are the three filters that we want
10+
// only one author can be selected at a time, clicking on a different authoir will unclick the first author
11+
// implement search bar in author filter
12+
// implement
13+
14+
class Filter extends Component {
15+
constructor(props) {
16+
super(props);
17+
this.state = {
18+
author: null,
19+
status: [],
20+
tags: [],
21+
search: "",
22+
allTags: Array.from(
23+
new Set(
24+
props.tickets.reduce(
25+
(acc, curr) => [...acc, ...(curr.tags || [])],
26+
[]
27+
)
28+
)
29+
),
30+
allAuthors: Array.from(
31+
new Set(
32+
props.tickets.map((ticket) =>
33+
JSON.stringify({
34+
id: ticket.createdBy.id,
35+
name: ticket.createdBy.name,
36+
})
37+
)
38+
)
39+
),
40+
tickets: props.tickets,
41+
};
42+
this.allTickets = props.tickets;
43+
}
44+
45+
filter = (remove) => {
46+
let filtered = this.props.tickets;
47+
if (this.state.search) {
48+
filtered = filtered.filter(
49+
(ele) => ele.title.toLowerCase().indexOf(this.state.search) !== -1
50+
);
51+
}
52+
if (this.state.author) {
53+
filtered = filtered.filter(
54+
(ele) => this.state.author === JSON.stringify(ele.createdBy)
55+
);
56+
}
57+
if (this.state.status.length) {
58+
filtered = filtered.filter(
59+
(ele) => this.state.status.indexOf(ele.status) !== -1
60+
);
61+
}
62+
console.log(filtered);
63+
this.props.setFiltered(filtered);
64+
};
65+
66+
handleSearchBarChange = (evt) => {
67+
const currentValue = evt.target.value;
68+
this.setState(
69+
{
70+
search: currentValue,
71+
},
72+
this.filter
73+
);
74+
};
75+
76+
handleAuthorChange = (author) => {
77+
if (author === this.state.author) {
78+
console.log("Same author clicked twice");
79+
this.setState({ author: null }, this.filter);
80+
} else {
81+
this.setState({ author: author }, this.filter);
82+
}
83+
};
84+
85+
handleTagsChange = (tag) => {
86+
// in not present then add if already present then clicking on it will remove it
87+
if (this.state.tags.indexOf(tag) === -1) {
88+
this.setState({
89+
tags: [...this.state.tags, tag],
90+
});
91+
} else {
92+
this.setState({
93+
tags: [...this.state.tags.filter((ele) => ele === tag)],
94+
});
95+
}
96+
};
97+
98+
handleStatusChange = (status) => {
99+
// in not present then add if already present then clicking on it will remove it
100+
if (this.state.status.indexOf(status) === -1) {
101+
this.setState(
102+
{
103+
status: [...this.state.status, status],
104+
},
105+
this.filter
106+
);
107+
} else {
108+
this.setState(
109+
{
110+
status: [...this.state.status.filter((ele) => ele !== status)],
111+
},
112+
this.filter
113+
);
114+
}
115+
};
116+
117+
clearFilters = () => {
118+
this.setState(
119+
{
120+
search: "",
121+
author: null,
122+
status: ["OPEN", "CLOSED", "PENDING", "SOLVED", "ON_HOLD"],
123+
},
124+
this.props.clear
125+
);
126+
};
127+
128+
componentDidMount() {
129+
// fetch all labels and all
130+
}
131+
132+
render() {
133+
// console.log(this.state.allAuthors);
134+
const allStatus = [
135+
{ label: "Open", status: "OPEN" },
136+
{ label: "Closed", status: "CLOSED" },
137+
{ label: "Pending", status: "PENDING" },
138+
{ label: "Solved", status: "SOLVED" },
139+
{ label: "On Hold", status: "ON_HOLD" },
140+
];
141+
return (
142+
<div className="tickets-dashboard-filter">
143+
<div className="searchbar-container">
144+
<div className="searchbar">
145+
<span className="searchbar-icon">
146+
<SearchOutlinedIcon />
147+
</span>
148+
<Form>
149+
<Form.Control
150+
as="input"
151+
value={this.state.search}
152+
placeholder="Search Tickets"
153+
onChange={this.handleSearchBarChange}
154+
/>
155+
</Form>
156+
</div>
157+
<Button onClick={() => this.toggleNewTicketEditor(true)}>
158+
New Ticket
159+
</Button>
160+
</div>
161+
<div className="filters">
162+
<div onClick={this.clearFilters} className="clear-filters">
163+
{this.state.search
164+
? "Clear Search and Filters"
165+
: !this.state.author ||
166+
!this.state.tags.length ||
167+
(!this.state.status && "Clear Filters")}
168+
</div>
169+
<Dropdown size="sm" alignRight>
170+
<Dropdown.Toggle variant="light" id="dropdown-basic">
171+
Authors
172+
</Dropdown.Toggle>
173+
<Dropdown.Menu>
174+
{this.state.allAuthors.map((ele, index) => (
175+
<Dropdown.Item
176+
key={index}
177+
onClick={() => this.handleAuthorChange(ele)}
178+
>
179+
{this.state.author === ele && <CheckOutlinedIcon />}
180+
{JSON.parse(ele).name}
181+
</Dropdown.Item>
182+
))}
183+
</Dropdown.Menu>
184+
</Dropdown>
185+
<Dropdown size="sm" alignRight>
186+
<Dropdown.Toggle variant="light" id="dropdown-basic">
187+
Status
188+
</Dropdown.Toggle>
189+
<Dropdown.Menu>
190+
{allStatus.map((ele, index) => (
191+
<Dropdown.Item
192+
key={index}
193+
onClick={() => this.handleStatusChange(ele.status)}
194+
>
195+
{this.state.status.indexOf(ele.status) !== -1 && (
196+
<CheckOutlinedIcon />
197+
)}
198+
{ele.label}
199+
</Dropdown.Item>
200+
))}
201+
</Dropdown.Menu>
202+
</Dropdown>
203+
</div>
204+
</div>
205+
);
206+
}
207+
}
208+
209+
export default Filter;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.tickets-dashboard-filter {
2+
display: flex;
3+
flex-direction: column;
4+
.filters {
5+
display: flex;
6+
justify-content: flex-end;
7+
align-items: center;
8+
.clear-filters {
9+
cursor: pointer;
10+
margin-right: 10px;
11+
}
12+
}
13+
}

src/user/Admin/Tickets/TicketContent/BadgeElement.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@ const BadgeElement = (props) => {
55
let badgeVariant = null;
66
let text = null;
77
switch (props.ticketState) {
8-
case "open":
8+
case "OPEN":
99
badgeVariant = "primary";
1010
text = "Open"
1111
break;
12-
case "solved":
12+
case "SOLVED":
1313
badgeVariant = "success";
1414
text = "Solved"
1515
break;
16-
case "onHold":
16+
case "ON_HOLD":
1717
badgeVariant = "secondary";
1818
text = "On Hold"
1919
break;
20-
case "pending":
20+
case "PENDING":
2121
badgeVariant = "warning";
2222
text = "Pending"
2323
break;
24-
case "closed":
24+
case "CLOSED":
2525
badgeVariant = "danger";
2626
text = "Closed"
2727
break;

src/user/Admin/Tickets/TicketContent/TicketContent.js

Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,45 @@
11
import React, { Component } from "react";
22
import "./TicketContent.scss";
3-
import Moment from 'react-moment';
3+
import Moment from "react-moment";
44
import { Image } from "react-bootstrap";
5-
import BadgeElement from './BadgeElement';
5+
import BadgeElement from "./BadgeElement";
66
import { withRouter } from "react-router-dom";
77
import DataTable from "react-data-table-component";
8-
import customStyles from './DataTableCustomStyles';
8+
import customStyles from "./DataTableCustomStyles";
99
import userIcon2 from "../../../../assets/images/userIcon2.jpg";
1010

1111
class TicketContent extends Component {
12-
1312
handleRowClick = (arg1) => {
14-
console.log("Row Clicked!")
15-
console.log(arg1)
16-
this.props.viewTicket(arg1._id)
17-
}
13+
console.log("Row Clicked!");
14+
console.log(arg1);
15+
this.props.viewTicket(arg1._id);
16+
};
1817

1918
render() {
20-
2119
const CustomTitle = ({ row }) => (
22-
<div>
23-
{/* eslint-disable-next-line react/prop-types */}
24-
<div style={{ fontWeight: "bold" }}>{row.title}</div>
20+
<div className="Ticket-dashboard-ticket">
21+
<div className="status">
22+
<BadgeElement ticketState={row.status} />
23+
</div>
24+
<div>
25+
<div className="Ticket-dashboard-title">{row.title}</div>
26+
<div className="Ticket-dashboard-shortDesciption">{`${row.shortDescription.slice(
27+
0,
28+
100
29+
)}...`}</div>
30+
</div>
2531
</div>
2632
);
2733

2834
const columns = [
2935
{
30-
name: "Title",
31-
grow: 2,
36+
name: "",
37+
grow: 5,
3238
selector: "title",
33-
sortable: true,
34-
maxWidth: "600px", // when using custom you should use width or maxWidth, otherwise, the table will default to flex grow behavior
3539
cell: (row) => <CustomTitle row={row} />,
3640
},
3741
{
38-
name: "Description",
39-
grow: 3,
40-
selector: "shortDescription",
41-
wrap: true,
42-
sortable: true,
43-
format: (row) => `${row.shortDescription.slice(0, 100)}...`,
44-
},
45-
{
46-
name: "Status",
47-
grow: 1,
48-
// eslint-disable-next-line react/no-array-index-key
49-
cell: (row) => (
50-
<div>
51-
<BadgeElement ticketState={row.status} />
52-
</div>
53-
),
54-
},
55-
{
56-
name: "User",
57-
grow: 1,
58-
sortable: true,
59-
selector: 'createdBy.name',
42+
selector: "createdBy.name",
6043
cell: (row) => (
6144
<div>
6245
<Image
@@ -71,25 +54,21 @@ class TicketContent extends Component {
7154
),
7255
},
7356
{
74-
name: "Created At",
57+
name: "Created",
7558
sortable: true,
76-
selector: 'createdAt',
59+
selector: "createdAt",
7760
cell: (row) => (
7861
<div>
7962
<Moment format="DD MMM YYYY">{row.createdAt}</Moment>
8063
</div>
81-
)
64+
),
8265
},
8366
{
8467
name: "Comments",
8568
sortable: true,
86-
selector: 'comments',
87-
cell: (row) => (
88-
<div>
89-
{row.comments}
90-
</div>
91-
)
92-
}
69+
selector: "comments",
70+
cell: (row) => <div>{row.comments}</div>,
71+
},
9372
];
9473

9574
return (

src/user/Admin/Tickets/TicketContent/TicketContent.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,16 @@
77
.profile-text {
88
display: inline;
99
}
10+
11+
.Ticket-dashboard-ticket{
12+
display: flex;
13+
align-items: center;
14+
.status {
15+
margin-right: 18px;
16+
}
17+
.Ticket-dashboard-title {
18+
font-weight: bold;
19+
font-size: 1.1rem;
20+
font-family: Inter;
21+
}
22+
}

0 commit comments

Comments
 (0)