Allow user to import tickets from JIRA via CSV upload

This commit is contained in:
Pijus Kamandulis
2024-10-19 17:07:17 +03:00
parent 904dbdee7c
commit b2e6b02b2e
14 changed files with 220 additions and 18 deletions

View File

@@ -47,7 +47,7 @@ const Estimation: React.FC = () => {
return (
<div className="flex h-full">
<TaskSidebar
className="w-64 overflow-y-scroll bg-gray-50 p-4 dark:bg-nero-800"
className="w-96 overflow-y-scroll bg-gray-50 p-4 dark:bg-nero-800"
tickets={tickets}
onSelectTicket={(ticket) => setActiveTicket(ticket.id)}
onAddTicket={() => setIsDrawerOpen(true)}

View File

@@ -14,12 +14,12 @@ const PlayerList: React.FC<PlayerListProps> = ({
title = 'Players',
}) => {
return (
<div className="w-full max-w-sm rounded-lg bg-white p-6 shadow-lg dark:bg-nero-800">
<div className="flex w-full max-w-sm flex-col justify-between rounded-lg bg-white p-6 shadow-lg dark:bg-nero-800">
<h2 className="mb-4 text-xl font-semibold text-gray-900 dark:text-gray-100">
{title}
</h2>
<ul className="max-h-48 divide-y divide-gray-300 overflow-y-auto dark:divide-gray-600">
<ul className="flex-grow divide-y divide-gray-300 overflow-y-auto dark:divide-gray-600">
{players.length > 0 ? (
players.map((player) => (
<li

View File

@@ -1,5 +1,8 @@
import { Card, GridList } from '../../../components';
import classNames from 'classnames';
import { Button, Card, Drawer, GridList } from '../../../components';
import { EstimationSessionTicket } from '../../../lib/types/entityModels';
import TicketImportForm from './TicketImportForm';
import { useState } from 'react';
interface TaskSidebarProps {
className?: string;
@@ -16,9 +19,16 @@ const TaskSidebar: React.FC<TaskSidebarProps> = ({
onAddTicket,
onEditTicket,
}) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const containerClassName = classNames(
className,
'flex flex-col justify-between',
);
return (
<div className={className}>
<div className={containerClassName}>
<GridList
className="no-scrollbar overflow-y-scroll"
items={tickets}
colNum={1}
itemComponent={({ item }) => (
@@ -30,8 +40,15 @@ const TaskSidebar: React.FC<TaskSidebarProps> = ({
onEdit={() => onEditTicket(item.id)}
/>
)}
addItemLabel="+ Add Ticket"
onAddItem={onAddTicket}
/>
<Button className="mt-2" fullWidth onClick={() => setIsDrawerOpen(true)}>
Import Tickets
</Button>
<Drawer isOpen={isDrawerOpen} onClose={() => setIsDrawerOpen(false)}>
<TicketImportForm onTicketsImported={() => setIsDrawerOpen(false)} />
</Drawer>
</div>
);
};

View File

@@ -0,0 +1,87 @@
import { useState } from 'react';
import {
handleTicketFileUpload,
TicketFileUploadResponse,
} from '../../../lib/parsers/ticketUpload';
import { EstimationSessionTicket } from '../../../lib/types/entityModels';
import { Button, Card, GridList, Loader } from '../../../components';
import HtmlEmbed from '../../../components/HtmlEmbed';
import { useEstimationContext } from '../../../lib/context/estimation';
interface TicketImportFormProps {
onTicketsImported: () => void;
}
const TicketImportForm: React.FC<TicketImportFormProps> = ({
onTicketsImported,
}) => {
const [error, setError] = useState<string>('');
const [tickets, setTickets] = useState<EstimationSessionTicket[]>([]);
const estimationContext = useEstimationContext();
if (!estimationContext) {
return <Loader center fullHeight />;
}
const { createTickets } = estimationContext;
const onParsedTickets = ({ tickets, error }: TicketFileUploadResponse) => {
if (error) {
setError(error);
}
setTickets(tickets);
};
const onCreateTickets = async () => {
await createTickets(tickets);
onTicketsImported();
};
return (
<div>
<h2 className="mb-4 text-xl font-semibold text-gray-900 dark:text-gray-100">
Upload Ticket List CSV
</h2>
<input
type="file"
accept=".csv"
onChange={(e) => {
setTickets([]);
setError('');
handleTicketFileUpload(e, onParsedTickets);
}}
className="mb-4 block w-full text-sm text-gray-900 dark:text-gray-100"
/>
{error && <p className="text-red-500">{error}</p>}
{tickets.length > 0 && (
<div className="mt-4">
<h3 className="mb-2 mt-2 text-lg font-medium text-gray-900 dark:text-gray-100">
Tickets to be imported
</h3>
<GridList
className="no-scrollbar overflow-y-scroll"
items={tickets}
colNum={1}
itemComponent={({ item }) => (
<Card
key={item.id}
title={item.name}
description={`Estimate: ${item.estimate || 'N/A'}`}
>
<HtmlEmbed className="h-16 w-full" body={item.content} />
</Card>
)}
/>
<Button className="mt-4" fullWidth onClick={onCreateTickets}>
Import
</Button>
</div>
)}
</div>
);
};
export default TicketImportForm;