Feat : Create a group of backend team and assign ticket to the respective backend member

This commit is contained in:
Tomosa Sarkar 2025-02-06 13:09:40 +05:30
parent d436ef05d8
commit b4da641769
21 changed files with 209 additions and 249 deletions

View File

@ -20,7 +20,8 @@ DB_PORT=5432
DB_NAME=crm DB_NAME=crm
DB_USER_NAME= DB_USER_NAME=
DB_USER_PASS DB_USER_PASS
#Mantis
MantisBT_Token=
#CBS #CBS
ONLINE_CBS_IP= ONLINE_CBS_IP=
ONLINE_CBS_PORT= ONLINE_CBS_PORT=
@ -119,6 +120,6 @@ Here we are using "mantisBT" (open source application) for ticket management sys
- Go to "My account". - Go to "My account".
- Then create a API token. - Then create a API token.
- Copy the API token. - Copy the API token.
- And paste the token to those `route.ts` file of `CRM Customer Module` where the mantisBT API is calling. - And paste the token in `.env.local` file in place of `MantisBT_Token`
- create development team's user as protected. - create development team's user as protected.
- create a project, and keep the name as "KCCB". - create a project, and keep the name as "KCCB".

View File

@ -14,7 +14,6 @@ async function queryUser() {
const response = await axios.get<User>('/api/user'); const response = await axios.get<User>('/api/user');
user = response.data; user = response.data;
} catch (error: AxiosError | any) { } catch (error: AxiosError | any) {
console.log(error);
notifications.show({ notifications.show({
color: 'red', color: 'red',
title: error.code, title: error.code,

View File

@ -7,6 +7,9 @@ export class Ticket {
// @PrimaryColumn("int") // @PrimaryColumn("int")
id!: number id!: number
@PrimaryColumn()
ticket_id! : number
@Column("varchar") @Column("varchar")
category_of_request! : string category_of_request! : string
@ -24,6 +27,9 @@ export class Ticket {
@Column('varchar') @Column('varchar')
created_by! : string created_by! : string
@Column('bigint')
customer_account_no! : number
@Column('varchar') @Column('varchar')
created_date! : string created_date! : string

View File

@ -11,7 +11,7 @@ class AppDataSource {
username: process.env.DB_USER_NAME, username: process.env.DB_USER_NAME,
password: process.env.DB_USER_PASS, password: process.env.DB_USER_PASS,
database: process.env.DB_NAME, database: process.env.DB_NAME,
//dropSchema: process.env.NODE_ENV === 'development', // dropSchema: process.env.NODE_ENV === 'development',
synchronize: process.env.NODE_ENV === 'development', synchronize: process.env.NODE_ENV === 'development',
logging: false, logging: false,
entities: [User,Ticket], entities: [User,Ticket],

View File

@ -21,7 +21,6 @@ export async function GET(request: Request) {
} }
catch (error) { catch (error) {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }

View File

@ -12,7 +12,6 @@ export async function POST(req: Request) {
const session = await getIronSession<SessionPayload>(cookies(), getSessionOptions()) const session = await getIronSession<SessionPayload>(cookies(), getSessionOptions())
const body = await req.json(); const body = await req.json();
const { AccNo } = body; const { AccNo } = body;
console.log(AccNo)
if (!AccNo || AccNo === '') { if (!AccNo || AccNo === '') {
return Response.json({ message: "Account No can't be empty" }) return Response.json({ message: "Account No can't be empty" })
} }
@ -25,18 +24,16 @@ export async function POST(req: Request) {
.getRawOne(); .getRawOne();
if (!user) if (!user)
return Response.json({ message: 'Please Enter Valid Account No Or contact to administrator.' }) return Response.json({ message: 'Please Enter Valid Account No Or contact to administrator.' },{status:404})
session.accountNo = user.bank_account_no; session.accountNo = user.bank_account_no;
session.TwoStepAuthentication = false; session.TwoStepAuthentication = false;
session.otp = NaN; session.otp = NaN;
session.expiryTime = NaN; session.expiryTime = NaN;
await session.save(); await session.save();
//console.log(session);
return Response.json({ ok: true }) return Response.json({ ok: true })
} catch (error) { } catch (error) {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }

View File

@ -15,7 +15,6 @@ export async function POST(request: Request) {
return Response.json({message: 'logout successfully'}); return Response.json({message: 'logout successfully'});
} catch (error) { } catch (error) {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }

View File

@ -31,7 +31,6 @@ export async function GET(request: Request) {
return Response.json({ done: true }); return Response.json({ done: true });
} catch (error) { } catch (error) {
console.error(error);
return Response.json("Failed", { status: 500 }); return Response.json("Failed", { status: 500 });
} }

View File

@ -21,16 +21,13 @@ export async function GET(request: Request , { params }: { params: { account_no:
if (user_mob_no.mobile_number == null) if (user_mob_no.mobile_number == null)
return Response.json({ message: "Mobile number is not updated.Please contact with administrator" }) return Response.json({ message: "Mobile number is not updated.Please contact with administrator" })
console.log(user_mob_no.mobile_number)
//session.otp = NaN; //session.otp = NaN;
//session.mobileNo = user_mob_no.mobile_number; //session.mobileNo = user_mob_no.mobile_number;
await session.save(); await session.save();
console.log(session);
return Response.json(user_mob_no) return Response.json(user_mob_no)
} }
catch (error) { catch (error) {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }

View File

@ -32,7 +32,6 @@ export async function GET(req: Request) {
session.expiryTime=Date.now() + 90 * 1000; session.expiryTime=Date.now() + 90 * 1000;
await session.save(); await session.save();
//console.log(session);
const maskedPartMobNo= 'x'.repeat(mobileNumber.toString().length -3); const maskedPartMobNo= 'x'.repeat(mobileNumber.toString().length -3);
const lastThreeDigit= mobileNumber.toString().slice(-3); const lastThreeDigit= mobileNumber.toString().slice(-3);
console.log( "OTP :" , otp); console.log( "OTP :" , otp);
@ -40,7 +39,6 @@ export async function GET(req: Request) {
} }
catch (error) { catch (error) {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }
@ -61,14 +59,12 @@ export async function POST(req: Request) {
if (!OTP || OTP === '' || Number.isNaN(OTP)) if (!OTP || OTP === '' || Number.isNaN(OTP))
return Response.json({ message: "OTP field can not be blank" }) return Response.json({ message: "OTP field can not be blank" })
console.log(session)
// Check for OTP expiry time // Check for OTP expiry time
if(Date.now()> session.expiryTime){ if(Date.now()> session.expiryTime){
session.otp =NaN; session.otp =NaN;
session.expiryTime=NaN; session.expiryTime=NaN;
await session.save(); await session.save();
} }
//console.log(session);
if(Number.isNaN(session.otp)) if(Number.isNaN(session.otp))
return Response.json({ error: "The OTP Session has timed out "},{status:401}) return Response.json({ error: "The OTP Session has timed out "},{status:401})
@ -77,12 +73,10 @@ export async function POST(req: Request) {
session.TwoStepAuthentication = true; session.TwoStepAuthentication = true;
await session.save(); await session.save();
//console.log(session);
return Response.json({ ok: true }) return Response.json({ ok: true })
} }
catch (error) catch (error)
{ {
console.error(error);
return Response.json(null, { status: 500 }) return Response.json(null, { status: 500 })
} }
} }

View File

@ -0,0 +1,83 @@
import { getIronSession } from "iron-session";
import { cookies } from "next/headers";
import AppDataSource from "@/app/_data/source/app-data-source";
import { User } from "@/app/_data/entities/User";
import SessionPayload from "../../auth/session/payload";
import getSessionOptions from "../../auth/session/options";
export async function GET(req: Request, { params }: { params: { ticket_id: number }})
{
try {
const session = await getIronSession<SessionPayload>(cookies(), getSessionOptions());
console.log("Session :",session);
if (!session.TwoStepAuthentication) {
return Response.json(null);
}
if (!session.accountNo) {
return Response.json({ message: `Please sign in before raise a ticket` })
}
const connection_crm = await AppDataSource.getConnection();
//const connection_mantis = await AppDataSource.getMantisConnection();
const userRepo = connection_crm.getRepository(User);
const user_table = await userRepo.createQueryBuilder('user')
.select(['first_name', 'middle_name', 'last_name'])
.where("user.bank_account_no = :AccountNo", { AccountNo: session.accountNo })
.getRawOne();
const user_name = (user_table.first_name ? user_table.first_name + ' ' : '') +
(user_table.middle_name ? user_table.middle_name + ' ' : '') +
(user_table.last_name ? user_table.last_name + ' ' : '');
const ticketRepo = connection_crm.getRepository(Ticket);
const ticket_list = await ticketRepo.createQueryBuilder('ticket')
.select(['id', 'category_of_request', 'nature_of_request', 'created_date','additional_info'])
.where("ticket.created_by = :created_by", { created_by: user_name }).getRawMany();
if(!ticket_list){return Response.json('No Details Found')}
const ticket_ids = ticket_list.map(ticket => ticket.id);
// Mantis-database fetching for "status" & "message"
const queryRunner = connection_mantis.createQueryRunner();
await queryRunner.connect();
let ticketResolution: { [key: number]: string } = {};
let ticketMessage: { [key: number]: string[] } = {};
for (const id of ticket_ids) {
// For status of ticket
const ticket_resolution = await queryRunner.query('SELECT resolution,handler_id FROM "kccb_bug" WHERE id = $1', [id]);
const ticket_status =ticket_resolution[0]?.resolution
const ticket_handler =ticket_resolution[0]?.handler_id
let status :string ='';
if(ticket_status === 10 && ticket_handler === 0){status = 'Open'}
if(ticket_handler > 0){status = 'In Progress'}
if(ticket_status === 20){status='Resolved'}
if(ticket_status === 30){status='Reopen'}
ticketResolution[id] = status
// For Message of ticket
const ticket_note = await queryRunner.query('SELECT id FROM "kccb_bugnote" WHERE bug_id = $1', [id]);
const ticket_note_ids =ticket_note.map((item:{id:number}) =>item.id)
for(const note_id of ticket_note_ids ){
const ticket_note_text = await queryRunner.query('SELECT note FROM "kccb_bugnote_text" WHERE id = $1', [note_id]);
if (!ticketMessage[id]) {
ticketMessage[id] = [];
}
ticketMessage[id].push(...ticket_note_text);
}
}
const result = ticket_list.map(item =>
(
{
...item,status:ticketResolution[item.id]||null,message: ticketMessage[item.id]||null
}
)
)
queryRunner.release();
return Response.json(result)
}
catch (error) {
return Response.json(null, { status: 500 });
}
}

View File

@ -0,0 +1,28 @@
import AppDataSource from "@/app/_data/source/app-data-source";
export async function assignUser(category_of_complaint :string) {
const connection_mantis = await AppDataSource.getMantisConnection();
let assign_team_id,random_assignee;
if(category_of_complaint === 'ATM Related'){
assign_team_id = 12 ;
}
if(category_of_complaint === 'Internet Banking'){
assign_team_id = 13 ;
}
if(category_of_complaint === 'Mobile Banking'){
assign_team_id = 14 ;
}
if(category_of_complaint === 'Others'){
assign_team_id = 15 ;
}
const queryRunner = connection_mantis.createQueryRunner();
await queryRunner.connect();
const query = await queryRunner.query('SELECT id,username FROM "kccb_user" WHERE access_level = $1', [assign_team_id]);
if(query.length == 0)
random_assignee = 1 ;
else{
const team_list =query.map((row: { id: any; }) =>row.id)
random_assignee = team_list[Math.floor(Math.random()* team_list.length)]
}
return random_assignee;
}

View File

@ -5,6 +5,7 @@ import SessionPayload from "../auth/session/payload";
import AppDataSource from "@/app/_data/source/app-data-source"; import AppDataSource from "@/app/_data/source/app-data-source";
import { Ticket } from "@/app/_data/entities/Ticket"; import { Ticket } from "@/app/_data/entities/Ticket";
import { User } from "@/app/_data/entities/User"; import { User } from "@/app/_data/entities/User";
import { assignUser } from "./assign_memeber";
export async function POST(request: Request) { export async function POST(request: Request) {
@ -16,36 +17,53 @@ export async function POST(request: Request) {
if (!session.accountNo) { if (!session.accountNo) {
return Response.json({ message: `Please sign in before raise a ticket` }) return Response.json({ message: `Please sign in before raise a ticket` })
} }
if (!session.TwoStepAuthentication) { if (!session.TwoStepAuthentication) {
return Response.json(null); return Response.json(null);
} }
//console.log(new Date().toLocaleString("en-IN"));
const URL = "http://localhost/crm_internal/api/rest/issues/";
const mantisBT_Token =process.env.MantisBT_Token || ""; //MantisBT Token
console.log(mantisBT_Token);
const body = await request.json(); const body = await request.json();
//request_category, nature_of_request, issue, message //request_category, nature_of_request, issue, message
const { summary, description, additional_information, steps_to_reproduce } = body; const { summary, description, additional_information, steps_to_reproduce } = body;
const project = { id: 1 }; const project = { id: 1 };
const requestBody = { ...body, project }; const requestBody = { ...body, project };
const result = await (await fetch(URL, { const response_for_issue_ticket = await (await fetch(`http://localhost/crm_internal/api/rest/issues/`, {
method: "POST", method: "POST",
headers: headers:
{ "Content-Type": "application/json", "Authorization": "M7xHFMiZcg_oPrGZoRY-kPoivoniK3BO" }, { "Content-Type": "application/json", "Authorization": mantisBT_Token },
body: JSON.stringify(requestBody) body: JSON.stringify(requestBody)
})) }))
if (result.status === 403) { if (response_for_issue_ticket.status === 403) {
return Response.json({ message: 'Invalid Token' }, { status: 403 }) return Response.json({ message: 'Invalid Token' }, { status: 403 })
} }
if (result.status === 400) { if (response_for_issue_ticket.status === 400) {
return Response.json({ message: 'Project not specified' }, { status: 400 }) return Response.json({ message: 'Project not specified' }, { status: 400 })
} }
if (result.status === 404) { if (response_for_issue_ticket.status === 404) {
return Response.json({ message: 'Project not found' }, { status: 404 }) return Response.json({ message: 'Project not found' }, { status: 404 })
} }
if (result.status === 429) { if (response_for_issue_ticket.status === 429) {
return Response.json({ message: 'Please raised ticket after sometime.' }, { status: 429 }) return Response.json({ message: 'Please raised ticket after sometime.' }, { status: 429 })
} }
// Assign backend team to the ticket
const issue_id = parseInt(response_for_issue_ticket.statusText);
const assign_person = await assignUser(summary);
const response_for_assignee_issue_ticket = await (await fetch(`http://localhost/CRM_Internal/api/rest/issues/${issue_id}`, {
method: "PATCH",
headers:
{ "Content-Type": "application/json", "Authorization": mantisBT_Token },
body: JSON.stringify({handler :{ id : assign_person }})
}))
if (response_for_assignee_issue_ticket.status === 403) {
return Response.json({ message: 'Invalid Token' }, { status: 403 })
}
if(response_for_assignee_issue_ticket.status ==200)
{
console.log("Assignee is assign against the ticket");
}
///CRM database management ///CRM database management
const connection_crm = await AppDataSource.getConnection(); const connection_crm = await AppDataSource.getConnection();
const userRepo = connection_crm.getRepository(User); const userRepo = connection_crm.getRepository(User);
@ -60,19 +78,19 @@ export async function POST(request: Request) {
const ticketRepo = connection_crm.getRepository(Ticket); const ticketRepo = connection_crm.getRepository(Ticket);
const ticket = new Ticket(); const ticket = new Ticket();
ticket.ticket_id = parseInt(response_for_issue_ticket.statusText);
ticket.category_of_request = summary; ticket.category_of_request = summary;
ticket.nature_of_request = description; ticket.nature_of_request = description;
ticket.additional_info = additional_information ticket.additional_info = additional_information
ticket.message = steps_to_reproduce; ticket.message = steps_to_reproduce;
ticket.created_by = user_name; ticket.created_by = user_name;
//ticket.created_date = new Date().toString(); ticket.customer_account_no = Number(session.accountNo);
ticket.created_date=new Date().toLocaleString("en-IN"); ticket.created_date=new Date().toLocaleString("en-IN");
await ticketRepo.save(ticket); await ticketRepo.save(ticket);
return Response.json({ message: `Ticket No:${result.statusText} created successfully` }) return Response.json({ message: `Ticket No:${response_for_issue_ticket.statusText} created successfully` })
} }
catch (error) { catch (error) {
console.error(error);
return Response.json(null, { status: 500 }); return Response.json(null, { status: 500 });
} }
} }
@ -84,30 +102,32 @@ export async function GET(request: Request) {
if (!session.TwoStepAuthentication) { if (!session.TwoStepAuthentication) {
return Response.json(null); return Response.json(null);
} }
if (!session.accountNo) { if (!session.accountNo) {
return Response.json({ message: `Please sign in before raise a ticket` }) return Response.json({ message: `Please sign in before raise a ticket` })
} }
const connection_crm = await AppDataSource.getConnection(); const connection_crm = await AppDataSource.getConnection();
const connection_mantis = await AppDataSource.getMantisConnection(); const connection_mantis = await AppDataSource.getMantisConnection();
const userRepo = connection_crm.getRepository(User); const userRepo = connection_crm.getRepository(User);
// Get full name of user
const user_table = await userRepo.createQueryBuilder('user') const user_table = await userRepo.createQueryBuilder('user')
.select(['first_name', 'middle_name', 'last_name']) .select(['first_name', 'middle_name', 'last_name'])
.where("user.bank_account_no = :AccountNo", { AccountNo: session.accountNo }) .where("user.bank_account_no = :AccountNo", { AccountNo: session.accountNo })
.getRawOne(); .getRawOne();
const user_name = (user_table.first_name ? user_table.first_name + ' ' : '') + const user_name = (user_table.first_name ? user_table.first_name + ' ' : '') +
(user_table.middle_name ? user_table.middle_name + ' ' : '') + (user_table.middle_name ? user_table.middle_name + ' ' : '') +
(user_table.last_name ? user_table.last_name + ' ' : ''); (user_table.last_name ? user_table.last_name + ' ' : '');
const ticketRepo = connection_crm.getRepository(Ticket); const ticketRepo = connection_crm.getRepository(Ticket);
// Get list of ticket raised by the user
const ticket_list = await ticketRepo.createQueryBuilder('ticket') const ticket_list = await ticketRepo.createQueryBuilder('ticket')
.select(['id', 'category_of_request', 'nature_of_request', 'created_date','additional_info']) .select(['id','ticket_id', 'category_of_request', 'nature_of_request', 'created_date','additional_info'])
.where("ticket.created_by = :created_by", { created_by: user_name }).getRawMany(); .where("ticket.created_by = :created_by" , { created_by: user_name })
.andWhere("ticket.customer_account_no = :customer_account_no" ,{customer_account_no:session.accountNo })
.getRawMany();
if(!ticket_list){return Response.json('No Details Found')} if(!ticket_list){return Response.json('No Details Found')}
const ticket_ids = ticket_list.map(ticket => ticket.id); const ticket_ids = ticket_list.map(ticket => ticket.ticket_id); // collect the ticket_id (ticket_id store the mantisBT's bug id)
// Mantis-database fetching for "status" & "message" // Mantis-database fetching for "status" & "message"
const queryRunner = connection_mantis.createQueryRunner(); const queryRunner = connection_mantis.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
@ -125,31 +145,31 @@ export async function GET(request: Request) {
if(ticket_status === 20){status='Resolved'} if(ticket_status === 20){status='Resolved'}
if(ticket_status === 30){status='Reopen'} if(ticket_status === 30){status='Reopen'}
ticketResolution[id] = status ticketResolution[id] = status
// For Message of ticket // For Message of ticket
const ticket_note = await queryRunner.query('SELECT id FROM "kccb_bugnote" WHERE bug_id = $1', [id]); const ticket_note = await queryRunner.query('SELECT bugnote_text_id FROM "kccb_bugnote" WHERE bug_id = $1', [id]);
const ticket_note_ids =ticket_note.map((item:{id:number}) =>item.id) const ticket_note_ids =ticket_note.map((item:{bugnote_text_id:number}) =>item.bugnote_text_id) // collect all ids from kccb_bugnote table for a issue
for(const note_id of ticket_note_ids ){ for(const note_id of ticket_note_ids ){
const ticket_note_text = await queryRunner.query('SELECT note FROM "kccb_bugnote_text" WHERE id = $1', [note_id]); const ticket_note_text = await queryRunner.query('SELECT note FROM "kccb_bugnote_text" WHERE id = $1', [note_id]);
if (!ticketMessage[id]) { if (!ticketMessage[id]) {
ticketMessage[id] = []; ticketMessage[id] = [];
} }
ticketMessage[id].push(...ticket_note_text); ticketMessage[id].push(...ticket_note_text);
} }
} }
const result = ticket_list.map(item => // merge message & status in the response
const response_for_issue_ticket = ticket_list.map(item =>
( (
{ {
...item,status:ticketResolution[item.id]||null,message: ticketMessage[item.id]||null ...item,status:ticketResolution[item.ticket_id]||null,message: ticketMessage[item.ticket_id]||null
} }
) )
) )
queryRunner.release(); queryRunner.release();
return Response.json(result) return Response.json(response_for_issue_ticket)
} }
catch (error) { catch (error) {
console.error(error);
return Response.json(null, { status: 500 }); return Response.json(null, { status: 500 });
} }
} }

View File

@ -24,15 +24,12 @@ export async function GET(request: Request) {
.select(["id","bank_account_no"]) .select(["id","bank_account_no"])
.where("user.bank_account_no = :userAccountNo", { userAccountNo: session.accountNo }) .where("user.bank_account_no = :userAccountNo", { userAccountNo: session.accountNo })
.getRawOne(); .getRawOne();
//console.log(session)
// if (!user_details || session.TwoStepAuthentication === false) // if (!user_details || session.TwoStepAuthentication === false)
if (!user_details) if (!user_details)
return Response.json({ message: "User_details not found" }, { status: 404 }); return Response.json({ message: "User_details not found" }, { status: 404 });
return Response.json(user_details); return Response.json(user_details);
} catch (error) { } catch (error) {
console.error(error);
return Response.json(null, { status: 500 }); return Response.json(null, { status: 500 });
} }
} }

View File

@ -20,7 +20,6 @@ async function logout() {
try { try {
await axios.post('/api/auth/logout'); await axios.post('/api/auth/logout');
} catch (error: AxiosError | any) { } catch (error: AxiosError | any) {
console.log(error);
notifications.show({ notifications.show({
color: 'red', color: 'red',
title: error.code, title: error.code,

View File

@ -6,10 +6,13 @@ export default function Home() {
return ( return (
<> <>
<p>Welcome to CRM Portal</p> <p>Welcome to CRM Portal</p>
<p>For Issue a ticket ,click on create ticket</p>
<UserContextConsumer> <UserContextConsumer>
{user => user && <p>Account No: {user.bank_account_no}</p>} {
user => user && <p><b>Your Present Login Account No: {user.bank_account_no}</b></p>
}
</UserContextConsumer> </UserContextConsumer>
<p><li>For raise a complain or assistance ,please click on <b>Create Ticket</b></li></p>
<p><li>For track a ticket ,please click on <b>View Ticket</b></li></p>
</> </>
); );
} }

View File

@ -107,7 +107,7 @@ const ComplaintForm: React.FC = () => {
} }
const requestBody = { const requestBody = {
summary: description, summary: category,
description: description, description: description,
additional_information: additionalInformation.join(", "), additional_information: additionalInformation.join(", "),
steps_to_reproduce: message, steps_to_reproduce: message,
@ -116,7 +116,6 @@ const ComplaintForm: React.FC = () => {
try { try {
const response = await axios.post("/api/ticket", requestBody); const response = await axios.post("/api/ticket", requestBody);
const data = await response.data; const data = await response.data;
console.log('API response:', data);
alert(data.message); alert(data.message);
window.location.reload(); window.location.reload();
} }
@ -146,9 +145,9 @@ const ComplaintForm: React.FC = () => {
<option value="" disabled> <option value="" disabled>
Select Category Select Category
</option> </option>
<option value="ATM">ATM Related</option> <option value="ATM Related">ATM Related</option>
<option value="IB">Internet Banking</option> <option value="Internet Banking">Internet Banking</option>
<option value="MB">Mobile Banking</option> <option value="Mobile Banking">Mobile Banking</option>
<option value="Others">Others</option> <option value="Others">Others</option>
</select> </select>
</label> </label>
@ -165,16 +164,16 @@ const ComplaintForm: React.FC = () => {
<option value="" disabled> <option value="" disabled>
Select Description Select Description
</option> </option>
{category === "ATM" && <option value="Transaction Issue">Transaction Issue</option>} {category === "ATM Related" && <option value="Transaction Issue">Transaction Issue</option>}
{category === "UPI" && <option value="UPI Issue">UPI Issue</option>} {/* {category === "UPI" && <option value="UPI Issue">UPI Issue</option>} */}
{category === "IB" && ( {category === "Internet Banking" && (
<> <>
{/* <option value="IMPS">IMPS Funds Transfer</option> */} {/* <option value="IMPS">IMPS Funds Transfer</option> */}
<option value="Fund Transfer failure">Fund Transfer Failure</option> <option value="Fund Transfer failure">Fund Transfer Failure</option>
<option value="Network Issue">Network Issue</option> <option value="Network Issue">Network Issue</option>
</> </>
)} )}
{category === "MB" && ( {category === "Mobile Banking" && (
<> <>
{/* <option value="IMPS">IMPS Funds Transfer</option> */} {/* <option value="IMPS">IMPS Funds Transfer</option> */}
<option value="Fund Transfer failure">Fund Transfer Failure</option> <option value="Fund Transfer failure">Fund Transfer Failure</option>

View File

@ -23,7 +23,7 @@ interface Message {
} }
interface Ticket { interface Ticket {
id: string; ticket_id: string;
category_of_request: string; category_of_request: string;
nature_of_request: string; nature_of_request: string;
created_date: string; created_date: string;
@ -42,7 +42,7 @@ export default function Page() {
useEffect(() => { useEffect(() => {
const sortedTickets = [...tickets].sort( const sortedTickets = [...tickets].sort(
// (a, b) => new Date(b.created_date).getTime() - new Date(a.created_date).getTime() // (a, b) => new Date(b.created_date).getTime() - new Date(a.created_date).getTime()
(a, b) => parseInt(b.id) - parseInt(a.id) (a, b) => parseInt(b.ticket_id) - parseInt(a.ticket_id)
); );
const results = sortedTickets.filter((ticket) => const results = sortedTickets.filter((ticket) =>
ticket.category_of_request.toLowerCase().includes(searchQuery.toLowerCase()) ticket.category_of_request.toLowerCase().includes(searchQuery.toLowerCase())
@ -69,18 +69,19 @@ export default function Page() {
}; };
//Add conditional row styles //Add conditional row styles
const getRowStyle = (status: string) => { // const getRowStyle = (status: string) => {
switch (status.toLowerCase()) { // switch (status.toLowerCase()) {
case "resolved": // case "resolved":
return { backgroundColor: "#e0e3df", color: "#000"}; // Grey for closed // return { backgroundColor: "#e0e3df", color: "#000"}; // Grey for closed
default: // default:
return {}; // return {};
} // }
}; // };
const rows = filteredTickets.map((ticket) => ( const rows = filteredTickets.map((ticket) => (
<Table.Tr key={ticket.id} style={getRowStyle(ticket.status)}> // <Table.Tr key={ticket.ticket_id} style={getRowStyle(ticket.status)}>
<Table.Td>{ticket.id}</Table.Td> <Table.Tr key={ticket.ticket_id}>
<Table.Td>{ticket.ticket_id}</Table.Td>
<Table.Td>{ticket.category_of_request}</Table.Td> <Table.Td>{ticket.category_of_request}</Table.Td>
<Table.Td>{ticket.nature_of_request}</Table.Td> <Table.Td>{ticket.nature_of_request}</Table.Td>
<Table.Td>{ticket.created_date}</Table.Td> <Table.Td>{ticket.created_date}</Table.Td>

View File

@ -1,159 +0,0 @@
"use client";
import {
ActionIcon,
Button,
Container,
Flex,
Group,
Modal,
Space,
Table,
TextInput,
Title,
Tooltip,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconMessage, IconSearch } from "@tabler/icons-react";
import { useEffect, useState } from "react";
interface Message {
note: string;
}
interface Ticket {
id: string;
category_of_request: string;
nature_of_request: string;
created_date: string;
status: string;
message: Message[];
}
export default function Page() {
const [opened, { open, close }] = useDisclosure(false);
const [tickets, setTickets] = useState<Ticket[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [filteredTickets, setFilteredTickets] = useState<Ticket[]>([]);
const [filteredUsers, setFilteredUsers] = useState<Ticket[]>([]);
const [activeMessages, setActiveMessages] = useState<Message[] | null>(null);
// Fetch tickets from the API
const fetchTickets = async () => {
try {
const response = await fetch("/api/ticket");
if (!response.ok) {
throw new Error("Failed to fetch tickets");
}
const data: Ticket[] = await response.json();
setTickets(data);
setFilteredTickets(data); // Set initial filtered tickets
} catch (error) {
console.error("Error fetching tickets:", error);
}
};
useEffect(() => {
fetchTickets(); // Call API when component mounts
}, []);
// Filter tickets based on the search query
useEffect(() => {
const results = tickets.filter((ticket) =>
ticket.category_of_request.toLowerCase().includes(searchQuery.toLowerCase())
);
setFilteredTickets(results);
}, [searchQuery, tickets]);
const handleOpenMessage = (messages: Message[]) => {
setActiveMessages(messages);
open();
};
const rows = filteredUsers.map((ticket) => (
<Table.Tr key={ticket.id}>
<Table.Td>{ticket.id}</Table.Td>
<Table.Td>{ticket.category_of_request}</Table.Td>
<Table.Td>{ticket.nature_of_request}</Table.Td>
<Table.Td>{ticket.created_date}</Table.Td>
<Table.Td>{ticket.status}</Table.Td>
<Table.Td>
<Group>
<ActionIcon
variant="subtle"
onClick={() => handleOpenMessage(ticket.message)}
>
<Tooltip label="Message">
<IconMessage />
</Tooltip>
</ActionIcon>
</Group>
</Table.Td>
</Table.Tr>
));
return (
<Container fluid>
<Title order={3}>View Ticket</Title>
<Space h="1rem" />
<Flex align="center" justify="space-between" w={720}>
<Flex align="center">
<Title order={5}>Tickets</Title>
<Space w="1rem" />
<TextInput
placeholder="Search"
value={searchQuery}
onChange={(event) => setSearchQuery(event.currentTarget.value)}
radius="md"
w={250}
leftSection={<IconSearch size={16} />}
/>
</Flex>
</Flex>
<Space h="1.5rem" />
<Table
w={720}
stickyHeader
stickyHeaderOffset={60}
withTableBorder
highlightOnHover
horizontalSpacing="lg"
>
<thead>
<tr>
<th>Ticket ID</th>
<th>Category</th>
<th>Description</th>
<th>Timestamp</th>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</Table>
<Modal opened={opened} onClose={close} title="Message">
<Flex direction="column" gap="sm">
{activeMessages?.map((msg, index) => (
<div
key={index}
style={{
padding: "0.5rem",
background: index % 2 === 0 ? "#f1f3f5" : "#dce6f2",
borderRadius: "8px",
maxWidth: "70%",
}}
>
{msg.note}
</div>
))}
<Button onClick={close} mt="sm">
Close
</Button>
</Flex>
</Modal>
</Container>
);
}

View File

@ -29,7 +29,6 @@ async function handleResendOTP() {
// Object.assign(response.data); // Object.assign(response.data);
alert(response.data.message); alert(response.data.message);
} catch (error: AxiosError | any) { } catch (error: AxiosError | any) {
console.log(error);
notifications.show({ notifications.show({
color: 'red', color: 'red',
title: error.code, title: error.code,
@ -49,13 +48,13 @@ async function handleValidateOTP(OtpInput: OtpInput) {
} }
Object.assign(Result, response.data); Object.assign(Result, response.data);
} catch (error: AxiosError | any) { } catch (error: AxiosError | any) {
console.log(error); alert(error.response.data.error);
notifications.show({ // notifications.show({
color: 'red', // color: 'red',
title: error.response.status, // title: error.response.status,
message: error.response.data.error // message: error.response.data.error
}) // })
} }
return Result; return Result;
} }

View File

@ -27,18 +27,17 @@ async function handleRequestOTP(SendAccNoInput: SendAccNoInput) {
if (response.data.ok) { if (response.data.ok) {
const otp_response = await axios.get("/api/otp"); const otp_response = await axios.get("/api/otp");
if (otp_response.status === 200) { if (otp_response.status === 200) {
// redirect('/otp');
window.location.href = 'login/otp' window.location.href = 'login/otp'
} }
} }
Object.assign(Result, response.data); Object.assign(Result, response.data);
} catch (error: AxiosError | any) { } catch (error: AxiosError | any) {
console.log(error); alert(error.response.data.message)
notifications.show({ // notifications.show({
color: 'red', // color: 'red',
title: error.response.status, // title: error.response.status,
message: error.response.data.message // message: error.response.data.message
}) // })
} }
return Result; return Result;
} }