Skip to content

Commit

Permalink
Merge pull request #972 from IkkiOcean/sale-page
Browse files Browse the repository at this point in the history
 Added Ticket Page with Ticket Management, Filters, and Header Banner
  • Loading branch information
manikumarreddyu authored Nov 10, 2024
2 parents 5758190 + f26027f commit 8fdaa10
Show file tree
Hide file tree
Showing 8 changed files with 396 additions and 1 deletion.
25 changes: 25 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/ConfirmationMessage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";

export default function ConfirmationMessage({
showConfirmation,
newTicketId,
setShowConfirmation,
}) {
return (
showConfirmation && (
<div
className="mb-8 bg-green-100 border-l-4 border-green-500 text-green-700 p-4"
role="alert"
>
<p className="font-bold">Ticket Submitted Successfully!</p>
<p>Your ticket ID is: {newTicketId}</p>
<button
onClick={() => setShowConfirmation(false)}
className="mt-2 text-sm text-green-800 hover:text-green-900"
>
Close
</button>
</div>
)
);
}
46 changes: 46 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/TicketDetailModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";

export default function TicketDetailModal({
selectedTicket,
setSelectedTicket,
}) {
return (
selectedTicket && (
<div
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full"
id="my-modal"
>
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div className="mt-3 text-center">
<h3 className="text-lg leading-6 font-medium text-gray-900">
Ticket Details
</h3>
<div className="mt-2 px-7 py-3">
<p className="text-sm text-gray-500">
<strong>ID:</strong> {selectedTicket.id}
<br />
<strong>Subject:</strong> {selectedTicket.subject}
<br />
<strong>Status:</strong> {selectedTicket.status}
<br />
<strong>Priority:</strong> {selectedTicket.priority}
<br />
<strong>Description:</strong> {selectedTicket.description}
<br />
<strong>Created At:</strong> {selectedTicket.createdAt}
<br />
<strong>Agent:</strong> {selectedTicket.agent}
</p>
</div>
<button
onClick={() => setSelectedTicket(null)}
className="mt-4 py-2 px-4 bg-green-500 text-white rounded-md"
>
Close
</button>
</div>
</div>
</div>
)
);
}
42 changes: 42 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/TicketFilters.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";

export default function TicketFilters({
filterStatus,
setFilterStatus,
filterPriority,
setFilterPriority,
searchTerm,
setSearchTerm,
}) {
return (
<div className="mb-4 flex flex-wrap gap-4">
<select
value={filterStatus}
onChange={(e) => setFilterStatus(e.target.value)}
className="rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
>
<option value="All">All Statuses</option>
<option value="Open">Open</option>
<option value="In-Progress">In-Progress</option>
<option value="Resolved">Resolved</option>
</select>
<select
value={filterPriority}
onChange={(e) => setFilterPriority(e.target.value)}
className="rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
>
<option value="All">All Priorities</option>
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
<input
type="text"
placeholder="Search by ID or Subject"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
/>
</div>
);
}
104 changes: 104 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/TicketForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useState } from "react";

export default function TicketForm({
newTicket,
handleInputChange,
handleSubmit,
}) {
const [selectedFile, setSelectedFile] = useState(null); // To store the selected file

const handleFileChange = (e) => {
const file = e.target.files[0]; // Get the first file selected by the user
if (file) {
setSelectedFile(file); // Store the file in state
}
};

return (
<div className="mb-12 bg-green-50 p-6 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold text-green-800 mb-4">
Create New Support Ticket
</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label
htmlFor="subject"
className="block text-sm font-medium text-gray-700"
>
Subject
</label>
<input
type="text"
id="subject"
name="subject"
value={newTicket.subject}
onChange={handleInputChange}
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
/>
</div>
<div>
<label
htmlFor="description"
className="block text-sm font-medium text-gray-700"
>
Description
</label>
<textarea
id="description"
name="description"
value={newTicket.description}
onChange={handleInputChange}
required
rows={4}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
></textarea>
</div>
<div>
<label
htmlFor="priority"
className="block text-sm font-medium text-gray-700"
>
Priority
</label>
<select
id="priority"
name="priority"
value={newTicket.priority}
onChange={handleInputChange}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500"
>
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
</div>
<div>
<label
htmlFor="file"
className="block text-sm font-medium text-gray-700"
>
Attach File
</label>
<input
type="file"
id="file"
onChange={handleFileChange}
className="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-green-50 file:text-green-700 hover:file:bg-green-100"
/>
{selectedFile && (
<div className="mt-2 text-sm text-gray-500">
<strong>Selected File:</strong> {selectedFile.name}
</div>
)}
</div>
<button
type="submit"
className="w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
Submit Ticket
</button>
</form>
</div>
);
}
60 changes: 60 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/TicketList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from "react";

export default function TicketList({ filteredTickets, setSelectedTicket }) {
return (
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
ID
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Subject
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Priority
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Agent
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Created At
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredTickets.map((ticket) => (
<tr
key={ticket.id}
onClick={() => setSelectedTicket(ticket)}
className="cursor-pointer hover:bg-gray-50"
>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{ticket.id}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.subject}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.status}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.priority}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.agent}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.createdAt}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
95 changes: 95 additions & 0 deletions frontend/src/AgroShopAI/components/Pages/TicketUser.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState, useEffect } from "react";
import TicketForm from "./TicketForm";
import ConfirmationMessage from "./ConfirmationMessage";
import TicketFilters from "./TicketFilters";
import TicketList from "./TicketList";
import TicketDetailModal from "./TicketDetailModal";
import HeaderBanner from "./Ticketbanner";
// Dummy ticket data
const dummyTickets = [
{ id: 'T001', subject: 'Order Delay', status: 'Open', priority: 'High', agent: 'John Doe', createdAt: '2023-05-10 09:30' },
{ id: 'T002', subject: 'Product Inquiry', status: 'In-Progress', priority: 'Medium', agent: 'Jane Smith', createdAt: '2023-05-09 14:15' },
{ id: 'T003', subject: 'Refund Request', status: 'Resolved', priority: 'Low', agent: 'Mike Johnson', createdAt: '2023-05-08 11:45' },
];

export default function SupportPage() {
const [newTicket, setNewTicket] = useState({
subject: "",
description: "",
priority: "Low",
});
const [showConfirmation, setShowConfirmation] = useState(false);
const [newTicketId, setNewTicketId] = useState(null);
const [filterStatus, setFilterStatus] = useState("All");
const [filterPriority, setFilterPriority] = useState("All");
const [searchTerm, setSearchTerm] = useState("");
const [tickets, setTickets] = useState(dummyTickets); // Set initial tickets from dummyTickets
const [selectedTicket, setSelectedTicket] = useState(null);
const [selectedFile, setSelectedFile] = useState(null); // To store the selected file

const handleInputChange = (e) => {
setNewTicket({ ...newTicket, [e.target.name]: e.target.value });
};

const handleFileChange = (e) => {
const file = e.target.files[0];
if (file) {
setSelectedFile(file); // Store the selected file in state
}
};

const handleSubmit = (e) => {
e.preventDefault();
const newTicketData = {
...newTicket,
id: `T${String(tickets.length + 1).padStart(3, '0')}`, // Generate a readable ticket ID
status: "Open", // Default status
agent: "Unassigned", // Placeholder for agent
createdAt: new Date().toLocaleString(), // Current date and time
file: selectedFile, // Attach the file to the new ticket
};
setTickets([newTicketData, ...tickets]);
setNewTicketId(newTicketData.id);
setShowConfirmation(true);
setNewTicket({ subject: "", description: "", priority: "Low" });
setSelectedFile(null); // Clear the file after submission
};

const filteredTickets = tickets.filter((ticket) => {
const statusMatch =
filterStatus === "All" || ticket.status === filterStatus;
const priorityMatch =
filterPriority === "All" || ticket.priority === filterPriority;
const searchMatch =
ticket.subject.toLowerCase().includes(searchTerm.toLowerCase()) ||
ticket.id.toString().includes(searchTerm);
return statusMatch && priorityMatch && searchMatch;
});

return (
<div className="container mx-auto px-4 py-6 mt-20 pt-15">
<HeaderBanner/>
<TicketForm
newTicket={newTicket}
handleInputChange={handleInputChange}
handleFileChange={handleFileChange}
handleSubmit={handleSubmit}
/>
<ConfirmationMessage
showConfirmation={showConfirmation}
newTicketId={newTicketId}
setShowConfirmation={setShowConfirmation}
/>
<TicketFilters
filterStatus={filterStatus}
setFilterStatus={setFilterStatus}
filterPriority={filterPriority}
setFilterPriority={setFilterPriority}
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
/>
<TicketList filteredTickets={filteredTickets} setSelectedTicket={setSelectedTicket} />
<TicketDetailModal selectedTicket={selectedTicket} setSelectedTicket={setSelectedTicket} />
</div>
);
}
Loading

0 comments on commit 8fdaa10

Please sign in to comment.