Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
- name: Generate version tag
id: version
run: |
TAG="v1.$(date -u +%y).$(date -u +%m%d).${{ github.run_number }}"
TAG="v1.$(date -u +%y).${{ github.run_number }}.$(date -u +%m%d)"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Version: $TAG"

Expand Down
9 changes: 8 additions & 1 deletion Components/Pages/AddJob.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@

<PageTitle>Add Job Listing</PageTitle>

<h1>Add Job Listing</h1>
<div class="d-flex justify-content-between align-items-center mb-3">
<h1>Add Job Listing</h1>
<AddContactDiscussionButton
ButtonClass="btn-outline-primary"
PrefilledJobTitle="@newJob.Title"
PrefilledCompany="@newJob.Company"
OnContactAdded="StateHasChanged" />
</div>

<div class="row">
<div class="col-12 mb-4">
Expand Down
1 change: 1 addition & 0 deletions Components/Pages/Contacts.razor
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<button class="btn btn-primary" @onclick="StartAddContact">
<i class="bi bi-person-plus"></i> Add Contact
</button>
<AddContactDiscussionButton ButtonClass="btn-outline-primary" OnContactAdded="LoadData" />
</div>
</div>

Expand Down
6 changes: 6 additions & 0 deletions Components/Pages/Jobs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<span> Crawl Jobs</span>
}
</button>
<AddContactDiscussionButton ButtonClass="btn-outline-primary" OnContactAdded="RefreshAfterContactAdded" />
<a href="/jobs/add" class="btn btn-success">
<i class="bi bi-plus-circle"></i> Add Job Manually
</a>
Expand Down Expand Up @@ -3095,6 +3096,11 @@ else
RefreshData();
}

private void RefreshAfterContactAdded()
{
RefreshData();
}

private void StartEditNotes()
{
editNotesText = selectedJob?.Notes ?? "";
Expand Down
94 changes: 1 addition & 93 deletions Components/Pages/JsaReport.razor
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@
<button class="btn btn-outline-secondary" @onclick="GenerateReport">
<i class="bi bi-arrow-clockwise"></i> Refresh
</button>
<button class="btn btn-outline-primary" @onclick="() => showAddContactModal = true">
<i class="bi bi-person-plus"></i> Add Contact/Discussion
</button>
<AddContactDiscussionButton ButtonClass="btn-outline-primary" OnContactAdded="GenerateReport" />
</div>
</div>

Expand Down Expand Up @@ -323,53 +321,6 @@
</div>
}

@if (showAddContactModal)
{
<div class="modal fade show" style="display: block; background: rgba(0,0,0,0.5);" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-person-plus"></i> Add Contact/Discussion</h5>
<button type="button" class="btn-close" @onclick="CloseAddContactModal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Job Title or Role</label>
<input type="text" class="form-control" @bind="newContactJobTitle" placeholder="e.g., Senior Developer" />
</div>
<div class="mb-3">
<label class="form-label">Company/Recruiter</label>
<input type="text" class="form-control" @bind="newContactCompany" placeholder="e.g., Tech Corp Ltd" />
</div>
<div class="mb-3">
<label class="form-label">Contact Name</label>
<input type="text" class="form-control" @bind="newContactName" placeholder="e.g., John Smith" />
</div>
<div class="mb-3">
<label class="form-label">Reason for Contact</label>
<input type="text" class="form-control" @bind="newContactReason" placeholder="e.g., Phone screening, Follow-up email" />
</div>
<div class="mb-3">
<label class="form-label">Result/Outcome</label>
<textarea class="form-control" rows="3" @bind="newContactResult" placeholder="e.g., Arranged interview for next week"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Date/Time (optional)</label>
<input type="datetime-local" class="form-control" @bind="newContactTimestamp" />
<small class="form-text text-muted">Leave blank to use current date/time</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseAddContactModal">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="SaveContactDiscussion" disabled="@(!IsContactFormValid)">
<i class="bi bi-save"></i> Save
</button>
</div>
</div>
</div>
</div>
}

@code {
private List<JsaReportGroup> reportGroups = new();
private JsaReportSummary? summary;
Expand All @@ -378,20 +329,6 @@
private string? exportError;
private string? emailSuccessMessage;

// Add Contact/Discussion modal
private bool showAddContactModal = false;
private string newContactJobTitle = "";
private string newContactCompany = "";
private string newContactName = "";
private string newContactReason = "";
private string newContactResult = "";
private DateTime? newContactTimestamp;

private bool IsContactFormValid =>
!string.IsNullOrWhiteSpace(newContactJobTitle) &&
!string.IsNullOrWhiteSpace(newContactCompany) &&
!string.IsNullOrWhiteSpace(newContactName);

// Filters
private DateTime? filterFromDate;
private DateTime? filterToDate;
Expand Down Expand Up @@ -681,35 +618,6 @@
}
}

private void CloseAddContactModal()
{
showAddContactModal = false;
newContactJobTitle = "";
newContactCompany = "";
newContactName = "";
newContactReason = "";
newContactResult = "";
newContactTimestamp = null;
}

private void SaveContactDiscussion()
{
if (!IsContactFormValid)
return;

HistoryService.RecordStandaloneContactDiscussion(
newContactJobTitle,
newContactCompany,
newContactName,
newContactReason,
newContactResult,
newContactTimestamp
);

CloseAddContactModal();
GenerateReport(); // Refresh the report to show the new entry
}

private static string GetActionBadgeClass(HistoryActionType action)
{
return action switch
Expand Down
120 changes: 120 additions & 0 deletions Components/Shared/AddContactDiscussionButton.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@using JobTracker.Models
@using JobTracker.Services
@inject JobHistoryService HistoryService

<button class="btn @ButtonClass" @onclick="() => showModal = true" title="@Title">
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trigger button doesn’t specify type="button". If this component is ever rendered inside a <form>/EditForm, the default type="submit" can cause unintended form submission. Setting type="button" on the trigger button avoids that class of bugs.

Suggested change
<button class="btn @ButtonClass" @onclick="() => showModal = true" title="@Title">
<button type="button" class="btn @ButtonClass" @onclick="() => showModal = true" title="@Title">

Copilot uses AI. Check for mistakes.
<i class="bi bi-person-plus"></i> @ButtonText
</button>

@if (showModal)
{
<div class="modal fade show" style="display: block; background: rgba(0,0,0,0.5);" tabindex="-1">
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most existing modals in the codebase use the pattern class="modal fade show d-block" (instead of relying on an inline display: block). For consistency with other pages (e.g., Jobs/Contacts modals), consider adding d-block here and using the same style conventions to reduce UI drift.

Suggested change
<div class="modal fade show" style="display: block; background: rgba(0,0,0,0.5);" tabindex="-1">
<div class="modal fade show d-block" style="background: rgba(0,0,0,0.5);" tabindex="-1">

Copilot uses AI. Check for mistakes.
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-person-plus"></i> Add Contact/Discussion</h5>
<button type="button" class="btn-close" @onclick="CloseModal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Job Title or Role</label>
<input type="text" class="form-control" @bind="jobTitle"
placeholder="e.g., Senior Developer" />
</div>
<div class="mb-3">
<label class="form-label">Company/Recruiter</label>
<input type="text" class="form-control" @bind="company"
placeholder="e.g., Tech Corp Ltd" />
</div>
<div class="mb-3">
<label class="form-label">Contact Name</label>
<input type="text" class="form-control" @bind="contactName"
placeholder="e.g., John Smith" />
</div>
<div class="mb-3">
<label class="form-label">Reason for Contact</label>
<input type="text" class="form-control" @bind="contactReason"
placeholder="e.g., Phone screening, Follow-up email" />
</div>
<div class="mb-3">
<label class="form-label">Result/Outcome</label>
<textarea class="form-control" rows="3" @bind="contactResult"
placeholder="e.g., Arranged interview for next week"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Date/Time (optional)</label>
<input type="datetime-local" class="form-control" @bind="timestamp" />
<small class="form-text text-muted">Leave blank to use current date/time</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseModal">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="SaveContact" disabled="@(!IsFormValid)">
<i class="bi bi-save"></i> Save
</button>
</div>
</div>
</div>
</div>
}

@code {
[Parameter] public string ButtonText { get; set; } = "Add Contact/Discussion";
[Parameter] public string ButtonClass { get; set; } = "btn-outline-primary";
[Parameter] public string Title { get; set; } = "Add contact or discussion entry";
[Parameter] public string? PrefilledJobTitle { get; set; }
[Parameter] public string? PrefilledCompany { get; set; }
[Parameter] public EventCallback OnContactAdded { get; set; }

private bool showModal = false;
private string jobTitle = "";
private string company = "";
private string contactName = "";
private string contactReason = "";
private string contactResult = "";
private DateTime? timestamp;

private bool IsFormValid =>
!string.IsNullOrWhiteSpace(jobTitle) &&
!string.IsNullOrWhiteSpace(company) &&
!string.IsNullOrWhiteSpace(contactName);

protected override void OnParametersSet()
{
if (!string.IsNullOrEmpty(PrefilledJobTitle))
jobTitle = PrefilledJobTitle;
if (!string.IsNullOrEmpty(PrefilledCompany))
company = PrefilledCompany;
}
Comment on lines +82 to +88
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnParametersSet assigns jobTitle/company whenever prefilled parameters are non-empty. Because Blazor re-applies parameters on parent re-renders, this can overwrite user edits while the modal is open (e.g., if the parent page re-renders for unrelated state changes). Consider only applying prefilled defaults when opening the modal (or only when showModal == false / the fields are still blank), so user-entered values aren’t unexpectedly reset.

Copilot uses AI. Check for mistakes.

private void CloseModal()
{
showModal = false;
jobTitle = PrefilledJobTitle ?? "";
company = PrefilledCompany ?? "";
contactName = "";
contactReason = "";
contactResult = "";
timestamp = null;
}

private async Task SaveContact()
{
if (!IsFormValid)
return;

HistoryService.RecordStandaloneContactDiscussion(
jobTitle,
company,
contactName,
contactReason,
contactResult,
timestamp
);

CloseModal();

if (OnContactAdded.HasDelegate)
await OnContactAdded.InvokeAsync();
}
}
8 changes: 6 additions & 2 deletions JobTracker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
<!-- Version: 1.YY.MMdd.BuildNumber — uses CI run number when available, HHmm locally -->
<!-- Version: 1.YY.BuildNumber.MMdd
MSI only compares the first 3 fields for upgrade detection (4th is ignored).
The build number (which changes every CI run) MUST be in the 3rd field,
otherwise same-day builds produce duplicate entries in Apps & Features.
CI uses github.run_number; local builds fall back to HHmm. -->
<BuildRevision Condition="'$(BUILD_NUMBER)' != ''">$(BUILD_NUMBER)</BuildRevision>
<BuildRevision Condition="'$(BUILD_NUMBER)' == ''">$([System.DateTime]::Now.ToString("HHmm"))</BuildRevision>
Comment on lines +12 to 14
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSI/Windows Installer version components are limited (notably the build field is max 65535). Using github.run_number directly in the 3rd field can eventually exceed this and break CI builds / MSI upgrades once the run number grows. Consider constraining BUILD_NUMBER to a safe range or switching to a monotonic value that’s guaranteed <= 65535 (e.g., derived from day-of-year + time bucket) while keeping the MSI upgrade semantics you want.

Suggested change
CI uses github.run_number; local builds fall back to HHmm. -->
<BuildRevision Condition="'$(BUILD_NUMBER)' != ''">$(BUILD_NUMBER)</BuildRevision>
<BuildRevision Condition="'$(BUILD_NUMBER)' == ''">$([System.DateTime]::Now.ToString("HHmm"))</BuildRevision>
CI uses github.run_number; local builds fall back to HHmm.
MSI limits each version component to 65535, so normalize the raw value
before placing it in the 3rd field. -->
<RawBuildRevision Condition="'$(BUILD_NUMBER)' != ''">$(BUILD_NUMBER)</RawBuildRevision>
<RawBuildRevision Condition="'$(BUILD_NUMBER)' == ''">$([System.DateTime]::Now.ToString("HHmm"))</RawBuildRevision>
<BuildRevision>$([System.Decimal]::ToInt32($([System.Decimal]::Remainder($([System.Decimal]::Parse('$(RawBuildRevision)')), 65535))))</BuildRevision>

Copilot uses AI. Check for mistakes.
<Version>1.$([System.DateTime]::Now.ToString("yy")).$([System.DateTime]::Now.ToString("MMdd")).$(BuildRevision)</Version>
<Version>1.$([System.DateTime]::Now.ToString("yy")).$(BuildRevision).$([System.DateTime]::Now.ToString("MMdd"))</Version>
<DefaultItemExcludes>$(DefaultItemExcludes);JobTracker.Tests\**</DefaultItemExcludes>
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion Portable/JobTracker-x64.zip.001
Git LFS file not shown
4 changes: 2 additions & 2 deletions Portable/JobTracker-x64.zip.002
Git LFS file not shown
2 changes: 1 addition & 1 deletion Portable/JobTracker-x86.zip.001
Git LFS file not shown
4 changes: 2 additions & 2 deletions Portable/JobTracker-x86.zip.002
Git LFS file not shown
2 changes: 1 addition & 1 deletion Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:8147;http://localhost:8147",
"applicationUrl": "https://localhost:8147;http://localhost:8148",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
Loading