Skip to content

Commit

Permalink
Issues are completed, descriptions are formatted, users are redirecte…
Browse files Browse the repository at this point in the history
…d after submitting, links to github
  • Loading branch information
jacoblurie29 committed Oct 12, 2023
1 parent 302e895 commit 111a507
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 52 deletions.
100 changes: 93 additions & 7 deletions components/Organizer/BugReportsTab/BugReportsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import { Button, Input, InputRef, Skeleton, Space, Table, Tag } from 'antd';
import { RequestType, useCustomSWR } from '../../../utils/request-utils';
import { Report } from '../../../types/client';
import { ColumnsType } from 'antd/lib/table';
import { SearchOutlined } from '@ant-design/icons';
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons';
import { FilterConfirmProps } from 'antd/es/table/interface';
import Highlighter from 'react-highlight-words';
import { useRef, useState } from 'react';
import { FilterValue } from 'antd/lib/table/interface';
import styles from '../../../styles/Report.module.css';

const BugReportsTab = () => {
const searchInput = useRef<InputRef>(null);
const [searchText, setSearchText] = useState('');
const [searchedColumn, setSearchedColumn] = useState('');
const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({});
const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({
status: ['OPEN', 'IN_PROGRESS'],
});

const { data: bugReports, error: bugReportsError } = useCustomSWR<Report[]>({
url: '/api/report',
Expand All @@ -26,6 +29,26 @@ const BugReportsTab = () => {

const bugReportsForTable = bugReports?.map(x => ({ ...x, key: x._id })) || ([] as Report[]);

const handleDeleteIssue = async (id: string | undefined) => {
if (!id) return;

if (confirm('Are you sure you want to delete this issue?\nThis will NOT delete the issue in GitHub!')) {
// delete the issue
const res = await fetch('/api/report', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ id }),
});
if (res.status === 200) {
window.location.reload();
} else {
alert('Failed to delete issue!');
}
}
};

const handleSearch = (
selectedKeys: string[],
confirm: (param?: FilterConfirmProps) => void,
Expand Down Expand Up @@ -85,7 +108,7 @@ const BugReportsTab = () => {
return recordValue.toString().toLowerCase().includes(value.toString().toLowerCase());
},
filteredValue:
(dataIndex in filteredInfo ? filteredInfo[dataIndex] : filteredInfo['application.' + dataIndex]) || null,
(dataIndex in filteredInfo ? filteredInfo[dataIndex] : filteredInfo['report.' + dataIndex]) || null,
onFilterDropdownOpenChange: (open: boolean) => {
if (open) {
setTimeout(() => searchInput.current?.select(), 100);
Expand Down Expand Up @@ -118,7 +141,7 @@ const BugReportsTab = () => {
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '15%',
width: '10%',
...getColumnSearchProps('name'),
},
{
Expand All @@ -132,7 +155,7 @@ const BugReportsTab = () => {
title: 'Description',
dataIndex: 'description',
key: 'description',
width: '40%',
width: '25%',
...getColumnSearchProps('description'),
},
{
Expand All @@ -142,7 +165,7 @@ const BugReportsTab = () => {
render: (date: string) => {
return <div>{new Date(date).toLocaleString()}</div>;
},
width: '20%',
width: '10%',
sorter: (a: Report, b: Report) => {
const aStart = new Date(a.date.toString());
const bStart = new Date(b.date.toString());
Expand All @@ -161,9 +184,72 @@ const BugReportsTab = () => {
filteredValue: filteredInfo['role'] || null,
onFilter: (value: string | number | boolean, record: any): boolean => record.role === value,
render: (role?: string) => {
return role !== undefined ? <Tag color="blue">{role === 'HACKER' ? 'Hacker' : 'Judge'}</Tag> : '';
return role !== undefined ? (
<Tag color="blue">{role === 'HACKER' ? 'Hacker' : role === 'JUDGE' ? 'Judge' : 'Organizer'}</Tag>
) : (
''
);
},
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
width: '10%',
defaultSortOrder: 'ascend',
filters: [
{ text: 'Open', value: 'OPEN' },
{ text: 'Closed', value: 'CLOSED' },
{ text: 'In Progress', value: 'IN_PROGRESS' },
],
filteredValue: filteredInfo['status'] || null,
onFilter: (value: string | number | boolean, record: any): boolean => record.status === value,
render: (status?: string) => {
return status !== undefined ? (
<Tag color={status === 'OPEN' ? 'red' : status === 'CLOSED' ? 'green' : 'yellow'}>
{status === 'OPEN' ? 'Open' : status === 'CLOSED' ? 'Closed' : 'In Progress'}
</Tag>
) : (
''
);
},
},
{
title: 'Issue #',
dataIndex: 'ghIssueNumber',
key: 'issueNumber',
width: '5%',
render: (issueNumber: number) => {
return issueNumber;
},
},
{
// github link
title: 'GitHub',
key: 'github',
width: '10%',
render: (text: string, record: Report) =>
record.ghUrl && (
<a
href={record.ghUrl}
target="_blank"
rel="noreferrer"
style={{ color: 'inherit', textDecoration: 'none' }}>
<Button className={styles.githubButton}>View on GitHub</Button>
</a>
),
},
{
// delete button
title: 'Actions',
key: 'action',
width: '5%',
render: (text: string, record: Report) => (
<Button className={styles.deleteButton} onClick={() => handleDeleteIssue(record._id)}>
<DeleteOutlined className={styles.deleteButtonIcon} />
</Button>
),
},
];

return (
Expand Down
11 changes: 11 additions & 0 deletions models/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ export const ReportSchema = new Schema({
status: {
type: String,
},
ghIssueNumber: {
type: Number,
required: true,
},
ghAssignee: {
type: String,
},
ghUrl: {
type: String,
required: true,
},
});

// prevent recompilation of model if it already exists
Expand Down
89 changes: 79 additions & 10 deletions pages/api/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,115 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
return res.status(200).send(reports);

case 'POST':
const { email, name, role, description, date, status } = req.body;

console.log(process.env.GITHUB_TOKEN);
const { email, name, role, description, date, status, ghAssignee } = req.body;

// Use the GitHub API to create an issue
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});

// make a more in depth description
// tell the developer this is a bug report, provide the email, name, role, time, and description.
// tell them that can track this issue in the organizer dashboard

const fullDescription = `# Bug Report
This is a bug report. You can track this issue in the organizer dashboard.
### Reporter Information
${email} - ${name} - ${role === 'ORGANIZER' ? 'Organizer' : role === 'JUDGE' ? 'Judge' : 'Hacker'}
Issue was reported on ${
new Date().toLocaleString('en-US', {
timeZone: 'America/Chicago',
}) + ' CT'
}
### Description
${description}`;

// Make a request to the GitHub API to create an issue
const response = await octokit.request('POST /repos/VandyHacks/witness/issues', {
owner: 'VandyHacks',
repo: 'witness',
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
title: 'Found a bug',
body: "I'm having a problem with this.",
assignees: ['jacoblurie29'],
title: `[BUG REPORT] Bug found by ${
role === 'ORGANIZER' ? 'an organizer' : role === 'JUDGE' ? 'a judge' : 'a hacker'
} on ${
new Date()
.toLocaleString('en-US', {
timeZone: 'America/Chicago',
})
.split(',')[0]
} at
${
new Date()
.toLocaleString('en-US', {
timeZone: 'America/Chicago',
})
.split(',')[1]
.split(':')[0] +
':' +
new Date()
.toLocaleString('en-US', {
timeZone: 'America/Chicago',
})
.split(',')[1]
.split(':')[1]
} ${
new Date()
.toLocaleString('en-US', {
timeZone: 'America/Chicago',
})
.split(' ')[2]
.split(' ')[0]
}`,
body: fullDescription,
assignees: [ghAssignee],
});

console.log(response);

// Create a new report in the database
const report = await Report.create({
email,
name,
role,
description,
date,
status,
ghIssueNumber: response.data.number,
ghAssignee: response.data.assignee.login,
ghUrl: response.data.html_url,
});
return res.status(200).send(report);

case 'PATCH':
const { id } = req.query;
const { id } = req.body;

if (!id) {
return res.status(400).send('No report ID provided');
}

const { status: newStatus } = req.body;

if (!newStatus) {
return res.status(400).send('No new status provided');
}

const updatedReport = await Report.findByIdAndUpdate(id, { status: newStatus }, { new: true });
return res.status(200).send(updatedReport);

case 'DELETE':
const { id: reportId } = req.query;
const { id: reportId } = req.body;

if (!reportId) {
return res.status(400).send('No report ID provided');
}

const deletedReport = await Report.findByIdAndDelete(reportId);

// Use the GitHub API to delete an issue
const octokit2 = new Octokit({
auth: process.env.GITHUB_TOKEN,
});

return res.status(200).send(deletedReport);

default:
Expand Down
Loading

0 comments on commit 111a507

Please sign in to comment.