generate report for pdf and excel completed
This commit is contained in:
parent
9c560a798c
commit
aacc23d5ef
5 changed files with 305 additions and 143 deletions
|
|
@ -1,10 +1,15 @@
|
|||
using Data;
|
||||
using iTextSharp.text;
|
||||
using iTextSharp.text.pdf;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Model;
|
||||
using OfficeOpenXml;
|
||||
using Services.Interaces;
|
||||
using Web.ViewModel.QuestionnaireVM;
|
||||
|
||||
|
||||
|
||||
namespace Web.Areas.Admin.Controllers
|
||||
{
|
||||
public class UserResponseStatusController : Controller
|
||||
|
|
@ -67,34 +72,269 @@ namespace Web.Areas.Admin.Controllers
|
|||
}
|
||||
|
||||
|
||||
//public async Task<IActionResult> UserResponsesStatus(string userEmail)
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DeleteSelected(string[] selectedEmails)
|
||||
{
|
||||
if (selectedEmails == null || selectedEmails.Length == 0)
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
var responsesToDelete = await _context.Responses
|
||||
.Where(r => selectedEmails.Contains(r.UserEmail))
|
||||
.ToListAsync();
|
||||
|
||||
if (responsesToDelete.Any())
|
||||
{
|
||||
_context.Responses.RemoveRange(responsesToDelete);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<IActionResult> GenerateReport(string userEmail, string format)
|
||||
{
|
||||
var responses = await _context.Responses
|
||||
.Include(r => r.Questionnaire)
|
||||
.Include(r => r.ResponseDetails)
|
||||
.ThenInclude(rd => rd.Question)
|
||||
.ThenInclude(q => q.Answers)
|
||||
.Include(r => r.ResponseDetails)
|
||||
.ThenInclude(rd => rd.ResponseAnswers)
|
||||
.Where(r => r.UserEmail == userEmail)
|
||||
.ToListAsync();
|
||||
|
||||
if (responses == null || !responses.Any())
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
switch (format.ToLower())
|
||||
{
|
||||
case "pdf":
|
||||
return GeneratePdfReport(responses);
|
||||
case "excel":
|
||||
return GenerateExcelReport(responses);
|
||||
default:
|
||||
return BadRequest("Unsupported report format.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IActionResult GeneratePdfReport(List<Response> responses)
|
||||
{
|
||||
var userName = responses.First().UserName;
|
||||
var userEmail = responses.First().UserEmail;
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var document = new Document(PageSize.A4, 50, 50, 25, 25);
|
||||
var writer = PdfWriter.GetInstance(document, stream);
|
||||
writer.CloseStream = false; // Prevent the stream from being closed when the document is closed
|
||||
|
||||
document.Open();
|
||||
|
||||
// Add a title
|
||||
var titleFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 18, BaseColor.BLACK);
|
||||
var title = new Paragraph($"Report for {userName} ({userEmail})", titleFont)
|
||||
{
|
||||
Alignment = Element.ALIGN_CENTER,
|
||||
SpacingAfter = 20
|
||||
};
|
||||
document.Add(title);
|
||||
|
||||
// Add a logo
|
||||
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.png");
|
||||
if (System.IO.File.Exists(logoPath))
|
||||
{
|
||||
var logo = Image.GetInstance(logoPath);
|
||||
logo.ScaleToFit(100f, 100f);
|
||||
logo.Alignment = Image.ALIGN_CENTER;
|
||||
document.Add(logo);
|
||||
}
|
||||
|
||||
// Add a table for each response
|
||||
var headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 14, BaseColor.WHITE);
|
||||
var cellFont = FontFactory.GetFont(FontFactory.HELVETICA, 12, BaseColor.BLACK);
|
||||
|
||||
foreach (var response in responses)
|
||||
{
|
||||
var table = new PdfPTable(2)
|
||||
{
|
||||
WidthPercentage = 100,
|
||||
SpacingBefore = 20,
|
||||
SpacingAfter = 20
|
||||
};
|
||||
table.SetWidths(new float[] { 1, 3 });
|
||||
|
||||
var cell = new PdfPCell(new Phrase($"Survey: {response.Questionnaire.Title}", headerFont))
|
||||
{
|
||||
Colspan = 2,
|
||||
BackgroundColor = new BaseColor(0, 150, 0),
|
||||
HorizontalAlignment = Element.ALIGN_CENTER,
|
||||
Padding = 10
|
||||
};
|
||||
table.AddCell(cell);
|
||||
|
||||
table.AddCell(new PdfPCell(new Phrase("Submitted on:", cellFont)) { Padding = 5 });
|
||||
table.AddCell(new PdfPCell(new Phrase(response.SubmissionDate.ToString(), cellFont)) { Padding = 5 });
|
||||
|
||||
foreach (var detail in response.ResponseDetails)
|
||||
{
|
||||
table.AddCell(new PdfPCell(new Phrase("Question:", cellFont)) { Padding = 5 });
|
||||
table.AddCell(new PdfPCell(new Phrase(detail.Question.Text, cellFont)) { Padding = 5 });
|
||||
|
||||
if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
|
||||
{
|
||||
table.AddCell(new PdfPCell(new Phrase("Answer:", cellFont)) { Padding = 5 });
|
||||
table.AddCell(new PdfPCell(new Phrase(detail.TextResponse, cellFont)) { Padding = 5 });
|
||||
}
|
||||
else
|
||||
{
|
||||
table.AddCell(new PdfPCell(new Phrase("Answers:", cellFont)) { Padding = 5 });
|
||||
var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text));
|
||||
table.AddCell(new PdfPCell(new Phrase(answers, cellFont)) { Padding = 5 });
|
||||
}
|
||||
}
|
||||
|
||||
document.Add(table);
|
||||
}
|
||||
|
||||
document.Close();
|
||||
writer.Close();
|
||||
|
||||
stream.Position = 0;
|
||||
return File(stream, "application/pdf", $"{userName}_report.pdf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IActionResult GenerateExcelReport(List<Response> responses)
|
||||
{
|
||||
var userName = responses.First().UserName;
|
||||
var userEmail = responses.First().UserEmail;
|
||||
|
||||
using (var package = new ExcelPackage())
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets.Add("Report");
|
||||
|
||||
// Add a logo
|
||||
var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.png");
|
||||
if (System.IO.File.Exists(logoPath))
|
||||
{
|
||||
var logo = new FileInfo(logoPath);
|
||||
var picture = worksheet.Drawings.AddPicture("Logo", logo);
|
||||
picture.SetPosition(0, 0, 0, 0);
|
||||
picture.SetSize(300, 70); // Adjust the size as needed
|
||||
}
|
||||
|
||||
// Add a title
|
||||
worksheet.Cells[6, 1].Value = $"Report for {userName} ({userEmail})";
|
||||
worksheet.Cells[6, 1, 6, 4].Merge = true;
|
||||
worksheet.Cells[6, 1, 6, 4].Style.Font.Size = 18;
|
||||
worksheet.Cells[6, 1, 6, 4].Style.Font.Bold = true;
|
||||
worksheet.Cells[6, 1, 6, 4].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
|
||||
|
||||
// Add headers
|
||||
worksheet.Cells[7, 1].Value = "Survey";
|
||||
worksheet.Cells[7, 2].Value = "Submitted on";
|
||||
worksheet.Cells[7, 3].Value = "Question";
|
||||
worksheet.Cells[7, 4].Value = "Response";
|
||||
|
||||
using (var range = worksheet.Cells[7, 1, 7, 4])
|
||||
{
|
||||
range.Style.Font.Bold = true;
|
||||
range.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
|
||||
range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
|
||||
range.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
|
||||
}
|
||||
|
||||
// Add data
|
||||
var row = 8;
|
||||
foreach (var response in responses)
|
||||
{
|
||||
worksheet.Cells[row, 1].Value = response.Questionnaire.Title;
|
||||
worksheet.Cells[row, 2].Value = response.SubmissionDate.ToString();
|
||||
row++;
|
||||
|
||||
foreach (var detail in response.ResponseDetails)
|
||||
{
|
||||
worksheet.Cells[row, 3].Value = detail.Question.Text;
|
||||
|
||||
if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
|
||||
{
|
||||
worksheet.Cells[row, 4].Value = detail.TextResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text));
|
||||
worksheet.Cells[row, 4].Value = answers;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
|
||||
worksheet.Cells.AutoFitColumns();
|
||||
|
||||
var stream = new MemoryStream();
|
||||
package.SaveAs(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{userName}_report.xlsx");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//private IActionResult GenerateExcelReport(List<Response> responses)
|
||||
//{
|
||||
// var responses = await _context.Responses
|
||||
// .Include(r => r.Questionnaire)
|
||||
// .Include(r => r.ResponseDetails)
|
||||
// .ThenInclude(rd => rd.Question)
|
||||
// .Include(r => r.ResponseDetails)
|
||||
|
||||
// .ThenInclude(rd => rd.ResponseAnswers)
|
||||
// .Where(r => r.UserEmail == userEmail)
|
||||
// .ToListAsync();
|
||||
|
||||
// if (responses == null || !responses.Any())
|
||||
// {
|
||||
// return NotFound();
|
||||
// }
|
||||
|
||||
// var userName = responses.First().UserName;
|
||||
// var userEmail = responses.First().UserEmail;
|
||||
|
||||
// var viewModel = new UserResponsesViewModel
|
||||
// using (var package = new ExcelPackage())
|
||||
// {
|
||||
// UserName = userName,
|
||||
// UserEmail = userEmail,
|
||||
// Responses = responses
|
||||
// };
|
||||
// var worksheet = package.Workbook.Worksheets.Add("Report");
|
||||
|
||||
// return View(viewModel);
|
||||
// worksheet.Cells[1, 1].Value = $"Report for {userName} ({userEmail})";
|
||||
// worksheet.Cells[2, 1].Value = "Survey";
|
||||
// worksheet.Cells[2, 2].Value = "Submitted on";
|
||||
// worksheet.Cells[2, 3].Value = "Question";
|
||||
// worksheet.Cells[2, 4].Value = "Response";
|
||||
|
||||
// var row = 3;
|
||||
// foreach (var response in responses)
|
||||
// {
|
||||
// worksheet.Cells[row, 1].Value = response.Questionnaire.Title;
|
||||
// worksheet.Cells[row, 2].Value = response.SubmissionDate.ToString();
|
||||
// row++;
|
||||
|
||||
// foreach (var detail in response.ResponseDetails)
|
||||
// {
|
||||
// worksheet.Cells[row, 3].Value = detail.Question.Text;
|
||||
|
||||
// if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
|
||||
// {
|
||||
// worksheet.Cells[row, 4].Value = detail.TextResponse;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text));
|
||||
// worksheet.Cells[row, 4].Value = answers;
|
||||
// }
|
||||
// row++;
|
||||
// }
|
||||
// row++;
|
||||
// }
|
||||
|
||||
// var stream = new MemoryStream();
|
||||
// package.SaveAs(stream);
|
||||
// stream.Position = 0;
|
||||
|
||||
// return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{userName}_report.xlsx");
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
@model IEnumerable<UserResponsesViewModel>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "User Responses status";
|
||||
ViewData["Title"] = "User Responses Status";
|
||||
}
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid mt-4 mb-5">
|
||||
<div class="col-10 offset-1">
|
||||
|
||||
|
|
@ -17,20 +15,22 @@
|
|||
<table class="table table-responsive w-100 d-block d-md-table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" id="selectAll" />
|
||||
</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Survey</th>
|
||||
|
||||
<th>Action</th>
|
||||
|
||||
<!-- Additional headers omitted for brevity -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
<input type="checkbox" name="selectedEmails" value="@item.UserEmail" class="selectCheckbox" />
|
||||
</td>
|
||||
<td>@item.UserName</td>
|
||||
<td>@item.UserEmail</td>
|
||||
<td>
|
||||
|
|
@ -44,12 +44,11 @@
|
|||
<td class="text-end">
|
||||
<a asp-controller="UserResponseStatus" asp-action="UserResponsesStatus" asp-route-UserEmail="@item.UserEmail" class="btn btn-info btn-sm"><i class="bi bi-eye"></i> View Responses status</a>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<button type="submit" class="btn btn-danger mt-2">Delete Selected</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
|
@ -57,6 +56,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
document.getElementById('selectAll').addEventListener('click', function (event) {
|
||||
var checkboxes = document.querySelectorAll('.selectCheckbox');
|
||||
checkboxes.forEach(function (checkbox) {
|
||||
checkbox.checked = event.target.checked;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: #007bff;
|
||||
background-color: #aed5ff;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 14px;
|
||||
|
|
@ -56,6 +56,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
<p>
|
||||
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
|
||||
|
|
@ -63,8 +65,17 @@
|
|||
|
||||
<div class="card p-5 m-3 shadow">
|
||||
<div class="bd-callout bd-callout-primary">
|
||||
<h4 class="text-primary">User Responses</h4>
|
||||
<text>@Model.UserName (@Model.UserEmail)</text>
|
||||
|
||||
<h6 class="text-primary font-weight-bold"><i class="bi bi-person"></i> @Model.UserName (@Model.UserEmail)</h6>
|
||||
<p class="text-info"><i class="bi bi-calculator"></i> Total responses: @Model.Responses.Count()</p>
|
||||
|
||||
|
||||
<a asp-action="GenerateReport" asp-route-userEmail="@Model.UserEmail" asp-route-format="pdf" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-filetype-pdf"></i> Generate PDF Report
|
||||
</a>
|
||||
<a asp-action="GenerateReport" asp-route-userEmail="@Model.UserEmail" asp-route-format="excel" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-file-excel"></i> Generate Excel Report
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Stepper -->
|
||||
|
|
@ -73,14 +84,15 @@
|
|||
{
|
||||
<div class="stepper-item">
|
||||
<div class="step-counter">
|
||||
<span class="badge bg-primary p-3 shadow">@response.Questionnaire.Title</span>
|
||||
<span class="badge bg-primary p-2 shadow">@response.Questionnaire.Title <i class="bi bi-arrow-right-circle-fill"></i></span>
|
||||
</div>
|
||||
<div class="step-content">
|
||||
|
||||
<div class="card p-4">
|
||||
<div class="step-header">
|
||||
<h6>Survey: @response.Questionnaire.Title</h6>
|
||||
<p>Submitted on: @response.SubmissionDate</p>
|
||||
<h6 class="text-primary font-weight-bold"><i class="bi bi-ui-checks"></i> @response.Questionnaire.Title</h6>
|
||||
<h6 class="text-success"><i class="bi bi-calendar2-week"></i> Submitted on: @response.SubmissionDate</h6>
|
||||
<p class="text-info"><i class="bi bi-question-square"></i> Total questions: @response.Questionnaire.Questions.Count()</p>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible button -->
|
||||
|
|
@ -154,104 +166,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
@* <div class="container-fluid mt-3">
|
||||
<p>
|
||||
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
|
||||
</p>
|
||||
|
||||
<div class="card p-5 m-3 shadow">
|
||||
<div class="bd-callout bd-callout-primary">
|
||||
<h4 class="text-primary">User Responses</h4>
|
||||
<text>@Model.UserName (@Model.UserEmail)</text>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@foreach (var response in Model.Responses)
|
||||
{
|
||||
<div class="container card mt-4 p-3">
|
||||
<div>
|
||||
<h6>Survey: @response.Questionnaire.Title</h6>
|
||||
<p>Submitted on: @response.SubmissionDate</p>
|
||||
|
||||
<!-- Collapsible button -->
|
||||
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#collapseResponse-@response.Id" aria-expanded="false" aria-controls="collapseResponse-@response.Id">
|
||||
View Responses
|
||||
</button>
|
||||
|
||||
<!-- Collapsible content -->
|
||||
<div class="collapse mt-3" id="collapseResponse-@response.Id">
|
||||
<table class="table table-responsive w-100 d-block d-md-table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Question</th>
|
||||
<th>Response</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var detail in response.ResponseDetails)
|
||||
{
|
||||
<tr>
|
||||
<td>@detail.Question.Text</td>
|
||||
<td>
|
||||
@if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
Question type
|
||||
<span class="badge text-bg-primary rounded-pill p-1s">@detail.QuestionType</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<br />
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
|
||||
Answer
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.TextResponse</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
|
||||
Question type
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.QuestionType</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<br />
|
||||
|
||||
<ul class="list-group">
|
||||
|
||||
@foreach (var answer in detail.ResponseAnswers)
|
||||
{
|
||||
|
||||
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Answer
|
||||
<span class="badge text-bg-primary rounded-pill p-1">@detail.Question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text</span>
|
||||
</li>
|
||||
}
|
||||
|
||||
</ul>
|
||||
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
|
||||
@section Scripts {
|
||||
<!-- Include Bootstrap 5 JS for collapse functionality -->
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EPPlus" Version="7.1.3" />
|
||||
<PackageReference Include="itext7" Version="8.0.4" />
|
||||
<PackageReference Include="iTextSharp" Version="5.5.13.3" />
|
||||
<PackageReference Include="MailJet.Api" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.3" />
|
||||
|
|
@ -33,7 +35,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Folder Include="NewFolder\" />
|
||||
<Folder Include="wwwroot\Images\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
BIN
Web/wwwroot/Images/logo.png
Normal file
BIN
Web/wwwroot/Images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
Loading…
Add table
Reference in a new issue