standardise table format

This commit is contained in:
misya 2025-05-07 14:38:28 +08:00
parent 7e80ca5ef9
commit 5ae31cd21b
5 changed files with 171 additions and 127 deletions

View File

@ -21,7 +21,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
_networkAccessService = networkAccessService; _networkAccessService = networkAccessService;
} }
private bool TryAccessNetworkPath() private bool TryAccessNetworkPath() //check if the path is accessible
{ {
try try
{ {
@ -71,6 +71,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
}) })
.ToList(); .ToList();
Console.WriteLine($"Marine Tarballs Count: {marineTarballs.Count}");
return View(marineTarballs); return View(marineTarballs);
} }
@ -109,7 +110,12 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
TarBallNo = marine.ClassifyID == "NO", TarBallNo = marine.ClassifyID == "NO",
IsSand = marine.ClassifyID == "SD", IsSand = marine.ClassifyID == "SD",
IsNonSandy = marine.ClassifyID == "NS", IsNonSandy = marine.ClassifyID == "NS",
IsCoquina = marine.ClassifyID == "CO" IsCoquina = marine.ClassifyID == "CO",
marine.OptionalName1,
marine.OptionalName2,
marine.OptionalName3,
marine.OptionalName4,
marine.FirstSampler
}).FirstOrDefault(); }).FirstOrDefault();
if (tarballData == null) if (tarballData == null)
@ -118,7 +124,7 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
// 2. Get photos from station folder (with date matching) // 2. Get photos from station folder (with date matching)
var sampleDateString = tarballData.DateSample.ToString("yyyyMMdd"); var sampleDateString = tarballData.DateSample.ToString("yyyyMMdd");
var stationFolder = Path.Combine(PhotoBasePath, tarballData.StationID); var stationFolder = Path.Combine(PhotoBasePath, tarballData.StationID);
var stationImages = new List<string>(); var stationImages = new Dictionary<string, string>();
if (Directory.Exists(stationFolder)) if (Directory.Exists(stationFolder))
{ {
@ -150,37 +156,33 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
"OPTIONAL04" "OPTIONAL04"
}; };
// Sort logic // Match images to their types
bool hasValidFormat = allImages.Any(f => foreach (var imageType in imageTypesInOrder)
imageTypesInOrder.Any(t => Path.GetFileNameWithoutExtension(f).ToUpper().Contains(t)));
if (hasValidFormat)
{ {
stationImages = allImages var matchedImage = allImages.FirstOrDefault(f =>
.OrderBy(f => Path.GetFileNameWithoutExtension(f).ToUpper().Contains(imageType));
if (matchedImage != null)
{ {
var fileName = Path.GetFileNameWithoutExtension(f).ToUpper(); stationImages[imageType] = matchedImage;
var typeIndex = imageTypesInOrder.FindIndex(t => fileName.Contains(t));
return typeIndex >= 0 ? typeIndex : int.MaxValue;
})
.ThenBy(f => f)
.Take(8)
.ToList();
} }
else
{
stationImages = allImages
.OrderBy(f => f)
.Take(8)
.ToList();
Console.WriteLine($"WARNING: No images matched keywords. Sorted alphabetically.");
} }
} }
// Validate minimum images // Validate mandatory images
if (stationImages.Count < 4) var mandatoryImages = new List<string>
{ {
return StatusCode(400, $"Minimum 4 images required for {tarballData.DateSample:yyyy/MM/dd}. Found: {stationImages.Count}"); "LEFTSIDECOASTALVIEW",
"RIGHTSIDECOASTALVIEW",
"DRAWINGVERTICALLINES",
"DRAWINGHORIZONTALLINES"
};
foreach (var mandatoryImage in mandatoryImages)
{
if (!stationImages.ContainsKey(mandatoryImage))
{
return StatusCode(400, $"Missing mandatory image: {mandatoryImage}");
}
} }
// 3. Generate PDF // 3. Generate PDF
@ -198,14 +200,19 @@ namespace PSTW_CentralSystem.Areas.MMS.Controllers
tarballData.IsSand, tarballData.IsSand,
tarballData.IsNonSandy, tarballData.IsNonSandy,
tarballData.IsCoquina, tarballData.IsCoquina,
stationImages.Count > 0 ? stationImages[0] : null, stationImages.ContainsKey("LEFTSIDECOASTALVIEW") ? stationImages["LEFTSIDECOASTALVIEW"] : null,
stationImages.Count > 1 ? stationImages[1] : null, stationImages.ContainsKey("RIGHTSIDECOASTALVIEW") ? stationImages["RIGHTSIDECOASTALVIEW"] : null,
stationImages.Count > 2 ? stationImages[2] : null, stationImages.ContainsKey("DRAWINGVERTICALLINES") ? stationImages["DRAWINGVERTICALLINES"] : null,
stationImages.Count > 3 ? stationImages[3] : null, stationImages.ContainsKey("DRAWINGHORIZONTALLINES") ? stationImages["DRAWINGHORIZONTALLINES"] : null,
stationImages.Count > 4 ? stationImages[4] : null, stationImages.ContainsKey("OPTIONAL01") ? stationImages["OPTIONAL01"] : null,
stationImages.Count > 5 ? stationImages[5] : null, stationImages.ContainsKey("OPTIONAL02") ? stationImages["OPTIONAL02"] : null,
stationImages.Count > 6 ? stationImages[6] : null, stationImages.ContainsKey("OPTIONAL03") ? stationImages["OPTIONAL03"] : null,
stationImages.Count > 7 ? stationImages[7] : null stationImages.ContainsKey("OPTIONAL04") ? stationImages["OPTIONAL04"] : null,
tarballData.OptionalName1,
tarballData.OptionalName2,
tarballData.OptionalName3,
tarballData.OptionalName4,
tarballData.FirstSampler
).GeneratePdf(); ).GeneratePdf();
// 4. Return file // 4. Return file

View File

@ -10,7 +10,9 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
string longitude, string latitude, DateTime dateSample, TimeSpan timeSample, string longitude, string latitude, DateTime dateSample, TimeSpan timeSample,
string classifyID, bool tarBallYes, bool tarBallNo, bool isSand, bool isNonSandy, string classifyID, bool tarBallYes, bool tarBallNo, bool isSand, bool isNonSandy,
bool isCoquina, string photoPath1, string photoPath2, string photoPath3, string photoPath4, bool isCoquina, string photoPath1, string photoPath2, string photoPath3, string photoPath4,
string photoPath5, string photoPath6, string photoPath7, string photoPath8 string photoPath5, string photoPath6, string photoPath7, string photoPath8,
string optionalName1, string optionalName2, string optionalName3, string optionalName4,
string firstSampler
) )
: IDocument : IDocument
{ {
@ -27,14 +29,19 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
private readonly bool _isSand = isSand; private readonly bool _isSand = isSand;
private readonly bool _isNonSandy = isNonSandy; private readonly bool _isNonSandy = isNonSandy;
private readonly bool _isCoquina = isCoquina; private readonly bool _isCoquina = isCoquina;
private readonly string? _photoPath1 = photoPath1; //having '?' makes it nullable, ELSE the string must always have a value private readonly string _photoPath1 = photoPath1;
private readonly string? _photoPath2 = photoPath2; //"but why keep '?' for photopath 1 to 4? private readonly string _photoPath2 = photoPath2;
private readonly string? _photoPath3 = photoPath3; //A: to make private readonly string _photoPath3 = photoPath3;
private readonly string? _photoPath4 = photoPath4; private readonly string _photoPath4 = photoPath4;
private readonly string? _photoPath5 = photoPath5; private readonly string? _photoPath5 = photoPath5;
private readonly string? _photoPath6 = photoPath6; private readonly string? _photoPath6 = photoPath6;
private readonly string? _photoPath7 = photoPath7; private readonly string? _photoPath7 = photoPath7;
private readonly string? _photoPath8 = photoPath8; private readonly string? _photoPath8 = photoPath8;
private readonly string? _optionalName1 = optionalName1;
private readonly string? _optionalName2 = optionalName2;
private readonly string? _optionalName3 = optionalName3;
private readonly string? _optionalName4 = optionalName4;
private readonly string _firstSampler = firstSampler;
private Image? LoadImage(string? imagePath) private Image? LoadImage(string? imagePath)
{ {
@ -286,8 +293,10 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
.Image(LoadImage(_photoPath6) ?? null) // Just pass null if no image .Image(LoadImage(_photoPath6) ?? null) // Just pass null if no image
.FitArea(); .FitArea();
table.Cell().Element(CellStyle).Text("Figure 5:").FontSize(12).AlignLeft(); table.Cell().Element(CellStyle).Text($"Figure 5: {_optionalName1}")
table.Cell().Element(CellStyle).Text("Figure 6:").FontSize(12).AlignLeft(); .FontSize(12).AlignLeft();
table.Cell().Element(CellStyle).Text($"Figure 6: {_optionalName2}")
.FontSize(12).AlignLeft();
table.Cell().Element(CellStyle).Height(150) table.Cell().Element(CellStyle).Height(150)
.Image(LoadImage(_photoPath7) ?? null) // Just pass null if no image .Image(LoadImage(_photoPath7) ?? null) // Just pass null if no image
@ -296,8 +305,10 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
.Image(LoadImage(_photoPath8) ?? null) // Just pass null if no image .Image(LoadImage(_photoPath8) ?? null) // Just pass null if no image
.FitArea(); .FitArea();
table.Cell().Element(CellStyle).Text("Figure 7:").FontSize(12).AlignLeft(); table.Cell().Element(CellStyle).Text($"Figure 7: {_optionalName3}")
table.Cell().Element(CellStyle).Text("Figure 8:").FontSize(12).AlignLeft(); .FontSize(12).AlignLeft();
table.Cell().Element(CellStyle).Text($"Figure 8: {_optionalName4}")
.FontSize(12).AlignLeft();
}); });
// Note Section // Note Section
@ -322,7 +333,12 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
columns.RelativeColumn(1); columns.RelativeColumn(1);
}); });
table.Cell().RowSpan(2).Element(CellStyle).Text("REPORTED BY :").Bold().FontSize(12); table.Cell().RowSpan(2).Element(CellStyle)
.Text(text =>
{
text.Span("REPORTED BY: ").Bold().FontSize(12);
text.Span(_firstSampler).FontSize(10);
});
table.Cell().Element(CellStyle).Text("Signature").FontSize(12); table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
table.Cell().Element(CellStyle).Text(""); table.Cell().Element(CellStyle).Text("");
table.Cell().Element(CellStyle).Text("Date").FontSize(12); table.Cell().Element(CellStyle).Text("Date").FontSize(12);
@ -331,7 +347,12 @@ namespace PSTW_CentralSystem.Areas.MMS.Models.PDFGenerator
table.Cell().Element(CellStyle).Text("Designation").FontSize(12); table.Cell().Element(CellStyle).Text("Designation").FontSize(12);
table.Cell().ColumnSpan(3).Element(CellStyle).Text(""); table.Cell().ColumnSpan(3).Element(CellStyle).Text("");
table.Cell().RowSpan(2).Element(CellStyle).Text("CHECKED BY :").Bold().FontSize(12); table.Cell().RowSpan(2).Element(CellStyle)
.Text(text =>
{
text.Span("CHECKED BY: ").Bold().FontSize(12);
text.Span("Rifaie Azhari").FontSize(10);
});
table.Cell().Element(CellStyle).Text("Signature").FontSize(12); table.Cell().Element(CellStyle).Text("Signature").FontSize(12);
table.Cell().ColumnSpan(2).Element(CellStyle).Text(""); table.Cell().ColumnSpan(2).Element(CellStyle).Text("");
//table.Cell().Element(CellStyle).Text("Date").FontSize(12); //table.Cell().Element(CellStyle).Text("Date").FontSize(12);

View File

@ -20,8 +20,8 @@
} }
div { div {
padding-top: 10px; padding-top: 5px;
padding-bottom: 10px; padding-bottom: 5px;
} }
h4 { h4 {
@ -38,39 +38,22 @@
border: 1px solid #ccc; border: 1px solid #ccc;
padding: 10px; padding: 10px;
} }
.tbhead {
text-align: center;
}
/* Default arrow style (grey) */
.sort-arrow {
color: #aaa; /* Light grey for default state */
font-size: 12px; /* Adjust size for better visibility */
line-height: 1; /* Ensure proper spacing */
display: inline-block; /* Stack arrows vertically */
margin: 0; /* Remove extra spacing */
}
/* Active arrow style (darker grey) */
.sort-arrow.active {
color: #333; /* Dark grey for active state */
font-weight: bold; /* Optional: Make it bold for emphasis */
}
</style> </style>
<!-- DataTables CSS -->
<link href="~/lib/datatables/datatables.css" rel="stylesheet" />
</head> </head>
<body> <body>
<div id="app" class="container"> <div id="app" class="container">
<div> <div>
<h4>Month</h4> <h4>Month</h4>
<select v-model="selectedMonth" style="width: 100%; padding: 5px;"> <select v-model="selectedMonth" style="width: 100%; padding: 5px;">
<option value="default" selected disabled>Filter by Month</option> <option value="" selected>Filter by Month</option>
<option v-for="month in months" :value="month">{{ month }}</option> <option v-for="month in months" :value="month">{{ month }}</option>
</select> </select>
<h4>Year</h4> <h4>Year</h4>
<select v-model="selectedYear" style="width: 100%; padding: 5px;"> <select v-model="selectedYear" style="width: 100%; padding: 5px;">
<option value="default" selected disabled>Filter by Year</option> <option value="" selected>Filter by Year</option>
<option v-for="year in years" :value="year">{{ year }}</option> <option v-for="year in years" :value="year">{{ year }}</option>
</select> </select>
@ -80,40 +63,17 @@
</div> </div>
<div class="datatable"> <div class="datatable">
<table> <table id="tarballTable" class="table table-bordered table-hover table-striped" style="width:100%;">
<thead> <thead>
<tr> <tr>
<th>No.</th> <th>No.</th>
<th v-on:click="sortBy('date')"> <th>Date</th>
Date <th>Station</th>
<span :class="['sort-arrow', { active: sortKey === 'date' && sortOrder === 'asc' }]">▲</span>
<span :class="['sort-arrow', { active: sortKey === 'date' && sortOrder === 'desc' }]">▼</span>
</th>
<th v-on:click="sortBy('station')">
Station
<span :class="['sort-arrow', { active: sortKey === 'station' && sortOrder === 'asc' }]">▲</span>
<span :class="['sort-arrow', { active: sortKey === 'station' && sortOrder === 'desc' }]">▼</span>
</th>
<th>Status</th> <th>Status</th>
<th>PDF</th> <th>PDF</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody></tbody>
<tr v-for="(data, index) in numberedData" :key="data.id">
<td>{{ index + 1 }}</td>
<td>{{ new Date(data.date).toLocaleDateString('en-GB') }}</td>
<td>{{ data.station }}</td>
<td>
<button class="btn btn-success">Approve</button>
<button class="btn btn-danger">Reject</button>
</td>
<td>
<a :href="`/MMS/Marine/ViewPDF?id=${data.id}`" class="btn btn-primary" target="_blank">View PDF</a>
<a :href="`/MMS/Marine/GenerateReport?id=${data.id}`" class="btn btn-primary">Download PDF</a>
</td>
</tr>
</tbody>
</table> </table>
</div> </div>
</div> </div>
@ -121,14 +81,14 @@
</html> </html>
@section Scripts { @section Scripts {
<!-- DataTables JS -->
<script src="~/lib/datatables/datatables.js"></script>
<script> <script>
new Vue({ new Vue({
el: '#app', el: '#app',
data: { data: {
selectedMonth: '', selectedMonth: '',
selectedYear: '', selectedYear: '',
sortKey: 'date',
sortOrder: 'desc',
months: [ months: [
'January', 'February', 'March', 'April', 'May', 'January', 'February', 'March', 'April', 'May',
'June', 'July', 'August', 'September', 'June', 'July', 'August', 'September',
@ -138,13 +98,28 @@
}, },
computed: { computed: {
years() { years() {
if (!this.dataFromServer || this.dataFromServer.length === 0) {
return []; // Return an empty array if no data is available
}
// Extract all years from the data
const allYears = this.dataFromServer.map(data => new Date(data.date).getFullYear()); const allYears = this.dataFromServer.map(data => new Date(data.date).getFullYear());
// Find the minimum and maximum years
const minYear = Math.min(...allYears); const minYear = Math.min(...allYears);
const maxYear = Math.max(...allYears); const maxYear = Math.max(...allYears);
// Generate a range of years from minYear to maxYear
return Array.from({ length: maxYear - minYear + 1 }, (_, i) => (minYear + i).toString()); return Array.from({ length: maxYear - minYear + 1 }, (_, i) => (minYear + i).toString());
}, },
filteredData() { sortedFilteredData() {
return this.dataFromServer.filter(data => { // If no filters are applied, return all data sorted by descending date
if (!this.selectedMonth && !this.selectedYear) {
return this.dataFromServer.sort((a, b) => new Date(b.date) - new Date(a.date));
}
// Filter data by selected month and year
const filtered = this.dataFromServer.filter(data => {
const date = new Date(data.date); const date = new Date(data.date);
const monthMatches = this.selectedMonth const monthMatches = this.selectedMonth
? date.toLocaleString('default', { month: 'long' }) === this.selectedMonth ? date.toLocaleString('default', { month: 'long' }) === this.selectedMonth
@ -154,33 +129,66 @@
: true; : true;
return monthMatches && yearMatches; return monthMatches && yearMatches;
}); });
},
numberedData() { // Sort data by date in descending order
return this.filteredData return filtered.sort((a, b) => new Date(b.date) - new Date(a.date));
.slice()
.sort((a, b) => {
const dateA = new Date(a.date);
const dateB = new Date(b.date);
return this.sortOrder === 'asc' ? dateA - dateB : dateB - dateA;
})
.map((data, index) => ({
no: index + 1,
...data
}));
} }
}, },
methods: { methods: {
clearFilters() { clearFilters() {
this.selectedMonth = ''; this.selectedMonth = '';
this.selectedYear = ''; this.selectedYear = '';
},
sortBy(key) {
if (this.sortKey === key) {
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
} else {
this.sortKey = key;
this.sortOrder = 'asc';
} }
},
mounted() {
// Initialize DataTables after Vue has rendered the table
this.$nextTick(() => {
const table = $('#tarballTable').DataTable({
"pageLength": 10,
"lengthMenu": [5, 10, 15, 20],
"responsive": true,
"order": [[1, "desc"]], // Default sorting by Date column (descending)
"orderMulti": false, // Disable multi-column sorting
"columns": [
{
"data": null,
"render": (data, type, row, meta) => meta.row + 1 // Dynamically generate "No."
},
{ "data": "date", "render": (data) => new Date(data).toLocaleDateString('en-GB') },
{ "data": "station" },
{
"data": null,
"render": () => `
<button class="btn btn-success">Approve</button>
<button class="btn btn-danger">Reject</button>
`
},
{
"data": null,
"render": (data) => `
<a href="/MMS/Marine/ViewPDF?id=${data.id}" class="btn btn-primary" target="_blank">View PDF</a>
<a href="/MMS/Marine/GenerateReport?id=${data.id}" class="btn btn-primary">Download PDF</a>
`
}
],
"rowCallback": function(row, data, index) {
// Update the "No." column to start from 1 for the current page
const pageInfo = table.page.info();
$('td:first', row).html(pageInfo.start + index + 1);
}
});
// Populate the table with all data on initial load
table.rows.add(this.dataFromServer).draw();
});
},
watch: {
sortedFilteredData() {
// Automatically update DataTables whenever the filtered data changes
const table = $('#tarballTable').DataTable();
table.clear();
table.rows.add(this.sortedFilteredData);
table.draw();
} }
} }
}); });

View File

@ -38,6 +38,10 @@ namespace PSTW_CentralSystem.DBContext
entity.Property(e => e.Longitude).HasColumnName("longitude"); entity.Property(e => e.Longitude).HasColumnName("longitude");
entity.Property(e => e.GetLatitude).HasColumnName("getLatitude"); entity.Property(e => e.GetLatitude).HasColumnName("getLatitude");
entity.Property(e => e.GetLongitude).HasColumnName("getLongitude"); entity.Property(e => e.GetLongitude).HasColumnName("getLongitude");
entity.Property(e => e.OptionalName1).HasColumnName("optionalName1");
entity.Property(e => e.OptionalName2).HasColumnName("optionalName2");
entity.Property(e => e.OptionalName3).HasColumnName("optionalName3");
entity.Property(e => e.OptionalName4).HasColumnName("optionalName4");
entity.Property(e => e.Timestamp).HasColumnName("timestamp"); entity.Property(e => e.Timestamp).HasColumnName("timestamp");
//Configure relationship with TarballStation //Configure relationship with TarballStation

View File

@ -17,15 +17,19 @@ namespace PSTW_CentralSystem.Models
public double GetLatitude { get; set; } // Maps to 'getLatitude' public double GetLatitude { get; set; } // Maps to 'getLatitude'
public double GetLongitude { get; set; } // Maps to 'getLongitude' public double GetLongitude { get; set; } // Maps to 'getLongitude'
public DateTime Timestamp { get; set; } // Maps to 'timestamp' public DateTime Timestamp { get; set; } // Maps to 'timestamp'
public string? OptionalName1 { get; set; }
public string? OptionalName2 { get; set; }
public string? OptionalName3 { get; set; }
public string? OptionalName4 { get; set; }
public required string PhotoPath1 { get; set; } // Left Side Coastal View public required string PhotoPath1 { get; set; } // Left Side Coastal View
public required string PhotoPath2 { get; set; } // Right Side Coastal View public required string PhotoPath2 { get; set; } // Right Side Coastal View
public required string PhotoPath3 { get; set; } // Vertical Lines public required string PhotoPath3 { get; set; } // Vertical Lines
public required string PhotoPath4 { get; set; } // Horizontal Lines public required string PhotoPath4 { get; set; } // Horizontal Lines
public required string PhotoPath5 { get; set; } // optional public string? PhotoPath5 { get; set; } // optional
public required string PhotoPath6 { get; set; } // optional public string? PhotoPath6 { get; set; } // optional
public required string PhotoPath7 { get; set; } // optional public string? PhotoPath7 { get; set; } // optional
public required string PhotoPath8 { get; set; } // optional public string? PhotoPath8 { get; set; } // optional
[ForeignKey("StationID")] [ForeignKey("StationID")]
public required MarineStation MarineStation { get; set; } public required MarineStation MarineStation { get; set; }