Patch Notes
Recent entries stay expanded, older dates collapse by default, and long update lists can be expanded per section. This keeps the latest work readable without losing the full project history.
Fixes
- Mining-op sync hot path batching: reduced mining-op sync churn by batching payout price lookups per op, replacing repeated ledger-row linear scans with keyed lookups in the ledger merge path, and deferring per-member sync saves into single batched op-level saves so each scheduler cycle does less repeated work.
- Mining-op live sync status push updates: active mining-op members now receive op-scoped SignalR status updates during scheduler work, including queued/behind-other-op messages, ledger refresh, payout recalculation, dashboard refresh, and retry warnings, so they can see exactly which sync step is taking time and report the stalled stage text if updates stop moving.
- Mining-op invite link sync flicker: the active mining-op invite URL now restores from an op-scoped client cache during partial refreshes instead of hiding and re-fetching on every sync-driven rerender, so the invite link no longer flashes in and out while the op is updating.
- Mining-op end transition false error toast: ending an operation now cancels any stale active-op retry chain on the client before the fallback refresh runs, so the UI can return to the
Start a New Operationpanel without also surfacing a bogus "unable to load/join" mining-op error toast. - Mining-op confirmations split into a dedicated queue: mining op confirmations now have their own page, with ready-to-confirm operations separated from recently ended operations still pending final processing. The queue now makes reconciliation wait states visible instead of hiding those ops, and
Confirm Allnow confirms only the ready operations while leaving still-processing ops in the queue. - Mining-op confirmations queue ordering: the dedicated mining-op confirmations page now shows the
Pending Final Processingsection first so officers see waiting reconciliation work before the ready-to-confirm actions. - Mining-op ore-id lookup cache: promoted the
Type_Id -> OreIdmapping used by ledger sync into a shared 24-hour memory cache so repeated mining-op sync runs no longer rebuild the ore lookup dictionary from the database each time a newLedgerProcessorinstance is created.
New Features
- Topbar app version badge: added a visible version badge to the upper-right top bar so the currently running app build is easy to confirm from any page.
- Publish-script auto version bump:
Scripts/Create-PublishZip.ps1now increments the app patch version inProperties/AssemblyInfo.csevery time the publish script runs, keeping packaged releases moving forward automatically without a separate manual edit.
Fixes
- Landing mining-op links: corrected the landing page mining-op CTA and related landing activity link to use
/Mining/MiningOpsinstead of/Home/MiningOps, so they open the full mining command page rather than a partial-only route.
Fixes
- Mining-op ledger timeout hard cap: hard-coded mining-ledger ESI requests to a
5second timeout so join/start flows fail fast on stalled ESI responses instead of waiting up to45seconds per character before continuing. - Bounty sync journal paging + timing diagnostics: bounty recording now fetches corp wallet journal pages with a short request timeout, stops paging once entries are older than the current bounty cutoff instead of re-reading the full journal every hour, uses a direct
CharacterId -> UserIdlookup during pilot aggregation, and logs per-corp bounty durations so slow corps are easier to identify from task heartbeat follow-up logs. - Mining-op join reliability + async ledger baseline: first-time joins now save the new member as active immediately, avoid null user navigation during baseline setup, and move per-character mining-ledger baseline initialization onto the background queue instead of blocking the join request until every linked character finishes or times out.
- Mining-op cumulative totals across active-op panels: the active mining-op ore breakdown, top-level gross value, member contribution values, refined totals, and snapshot-backed cards now all use cumulative
OpAmountvalues for the full op instead of mixing whole-op totals with last-sync deltas. - Mining-op viewer alt breakdown completeness: the
My Orescharacter breakdown now resolves the viewer’s full main/alt family and includes linked-character ledger attribution rows, so separately joined alts no longer disappear from the viewer summary. - PI storage volume lookup by current DB group IDs: fixed PI storage bars still showing
0 m³when cached launchpad contents used the local item import’s PI type IDs and group IDs. The PI page now recognizes the currentitems.group_idPI groups when resolving content volume, so launchpad/storage usage falls back to real PI volumes instead of zero, and the legacy launchpad/storage type-ID fallbacks now cover the actual PI structure variants in the local dataset.
Fixes
- PI storage-capacity totals: corrected PI storage facility capacity from
500 m³to12,000 m³in the storage-summary math so planet storage bars, used/capacity totals, and fill percentages now reflect the real storage facility capacity instead of showing inflated utilization. - PI POCO tax compile guard: initialized the PI storage-summary POCO export tax fallback rate before the
TryGetValue(...)branch soPlanetaryIndustryControllerno longer hits aCS0165"use of unassigned local variablepocoTaxRate" build failure. - Landing recent completed-op detail strip:
Home/Landingnow shows higher-signal summaries on recently completed mining ops, including confirmation state, total op value, payout/tax/fleet split (when present), and the ended timestamp so leaders can scan outcome details without opening the full op view. - Recruitment mail paging + cache acceleration:
Recruitment/GetMailsnow caches each viewed member mailbox for a short TTL, only resolves sender names for the currently rendered page, and serves the inbox in paged slices (25rows by default) instead of server-rendering every mail at once. The Recruitment CurrentMember mail tab now lazy-loads older pages with aLoad Older Mailaction,Recruitment/GetMailBodycaches recruiter-side body lookups for repeated opens, and both endpoints log cache-hit/fetch timing so slow ESI or oversized inboxes are easier to spot in production.
New Features
- PI full-image command-center mockup: added
mockups/pi-v1-full-image-command-center.html, a high-density PI dashboard concept showing colony health queue, route/feedability status, per-factory run/ETA forecasting, projected output value, pickup-tax estimates, and data-confidence indicators in one responsive view.
Fixes
- PI factory/reaction colony visibility: colony status now evaluates all timed PI pins (
ExpiryTime) rather than extractor-only timers, so factory-only reaction planets are now classified asActive,Expiring, orExpiredwhen running instead of being forced intoIdle. - PI topbar status chip parity: dashboard PI counters now include any timed PI pins (extractors plus factory/reaction cycles), so factory-only active colonies are reflected in the topbar status totals.
- PI card clarity for non-extractor colonies: factory-only colony cards now show a dedicated
Factory Timerssection when timers exist, and idle copy was updated so reaction-only setups no longer look like missing data. - Refresh-user server load control + runtime improvements:
TaskProcessor.RefreshUserInfonow uses bounded parallel execution for Discord role updates and token refresh processing (configurable viaRefreshUserInfoRoleUpdateParallelismandRefreshUserInfoTokenRefreshParallelism), token refresh runs against detached user snapshots to avoid cross-thread EF tracking contention, and token/affiliation helper paths were trimmed to remove redundant async scheduling and batching overhead. - Refresh-user Discord deep optimization pass: the refresh job now preloads Discord auth/title/scope config once per run, snapshots each guild once per server instead of repeatedly re-fetching role member lists, only runs title-role sync when mappings exist and the lower-frequency title cadence is due (
RefreshUserInfoTitleSyncIntervalHours, default4), skips bad-token users during title sync, reuses guild snapshots for recruitment cleanup, and logs per-phase/per-server timing and count metrics to make the long tail visible in production. - Notification sync runtime reduction:
GetNotificationsnow uses an async ESI notification path, fetches director and alt notification feeds with bounded parallelism (GetNotificationsFetchParallelism, default3), skips corps with no selected notification types before any ESI work, deduplicates merged notifications before formatting/broadcast, reuses cached name/item/price lookups across a corp batch, batchesLastNotificationChecksaves, and logs per-corp fetch/unique/new/sent counts with durations. - Mining-op scheduler fairness + heartbeat diagnostics: due mining-op work is now ordered by overdue
NextUpdatetime instead ofMiningOpId, mining-ledger timeout enforcement now uses the requested timeout path instead of silently falling back to an unlimited wait, and task heartbeat output now includes per-tasklongestDurplus an overallLongestTasksummary for production diagnostics.
Fixes
- Corp goal lifecycle auto-updates from incoming events:
MilestoneService.RecordEvent(...)now evaluates active corp goals for the event’s corp/metric and runs completion + milestone-threshold checks immediately, soCompletedUtcand 50%/75% notifications stay current without manual recalc. - Corp goal detail duplicate leaderboard computation:
HomeController.CorpGoalDetailnow reuses the already-loaded full leaderboard to resolve the current user row, removing a second full-board recomputation path. - Corp goal detail "you" highlighting reliability: leaderboard JSON now includes
userIdand the page matches rows against the viewer’s resolved main user id from the controller instead of fragile name-based matching. - Corp goal custom-range validation edge case: creating a
CustomRangegoal now rejectsstart == end, aligning with exclusive end-bound filtering (OccurredUtc < WindowEndUtc) so zero-length windows are not accepted.
New Features
- Corp Goal detail page: clicking a goal card on the Corp Goals list now opens a full detail view at
/Home/CorpGoalDetailshowing a stat strip (progress, remaining, contributor count, your rank), a full paginated leaderboard with alt-account counts, a recent events feed (last 50 contributions), and your personal position row highlighted inline. Admins see Recalculate and Enable/Disable buttons directly from the detail page. - Corp Goal Setup redesign: the setup form now uses a sectioned layout with a visual metric icon picker, a window tab bar (Lifetime / This Month / Rolling 30d / Custom), and autocomplete search fields for ship-type, victim-corp, and ore-type filters. The existing goals panel shows all goals in a card grid with per-card controls and a direct "View Detail" link.
- Corp Goals card navigation: each goal card on the Corp Goals list is now clickable and loads its detail page inline.
Fixes
- Mining-op alt re-auth targeting:
MiningOpAdAltnow resolves the requested warning-row alt within the current main+alt family and carries that expected user id in OAuth state, so re-auth links no longer silently target the wrong character. - Mining-op callback character guard: the
MiningOpScopecallback now validates the authorized character against the expected state user when provided and returns a clear error if a different character is authorized. - Mining-op auth warning panel cleanup: auth warning rows now describe mining-auth access requirements (instead of always saying “missing scope”), and clicking
Re-authorize altnow removes that specific row immediately before redirect to avoid stale warning carry-over. - Token refresh persistence reliability: refresh success now writes token fields to the tracked DB user row, preserves the existing refresh token when SSO omits
refresh_token, and syncs the caller copy from persisted values. This removes intermittent post-refresh auth drift that could make mining warnings reappear.
New Features
- Corp Goals: new corp-wide progress-tracking system. Officers create goals (ore mined, ships killed, bounty ISK, fleet participation, logistics) with configurable time windows and per-metric filters. A member-facing card grid at
/Home/CorpGoalsshows live progress bars and per-goal leaderboards with alt roll-up. Goal completion fires a Discord embed to the notifications channel; crossing 50% or 75% sends a milestone ping tracked viaLastNotifiedPctto prevent duplicate messages. - Corp Goal Setup: admin UI at
/Home/CorpGoalSetup(requiresLoyaltySetuppermission) for creating, enabling/disabling, deleting, and recalculating goals. Supports ore-type, victim-corp, ship-type, and fleet-type filters, and window types: lifetime, current month, rolling 30 days, or custom date range. A "Recalculate" button recomputes completion state from existing milestone events. - Fleet and logistics milestone event emission: fleet confirmations now emit
FleetsParticipated,FleetMinutes, andFleetsLedmilestone events (with fleet type); completing a logistics order emitsLogisticsOrdersCompletedandLogisticsVolumeHauledevents — both feed directly into Corp Goal progress.
Changes
- Corp Goals navigation wiring: added
Corp GoalsandCorp Goal Setupto theCorp Managementsidebar navigation so both pages are directly reachable from the main menu.Corp Goal Setupremains gated by the existingLoyaltySetuppermission. - Corp Goals migration-registration fix: added missing EF migration metadata for
20260403000010_20260403_AddCorpGoalsand20260403000020_AddCorpGoalLastNotifiedPctso EF now discovers and applies the corp-goal schema migrations. This resolves runtimeTable 'evehr.corpgoaldefinitions' doesn't existerrors when clicking the new Corp Goals buttons. - OAuth scope re-auth hardening:
BaseController.BuildFlowAuthorizationUrl(...)now automatically unions requested scopes with the user’s persisted DB scope set before building EVE SSO auth URLs, then normalizes/deduplicates the combined list. Re-auth flows now consistently request full existing scopes plus newly required scopes across controllers (with claim-scope fallback if DB scope lookup is unavailable). - Contract-validation prompt parity fix: updated the bulk-validation (
ValidateAllOrders) contract-item failure branch to call scope/relog prompt builders with the active user context, keeping prompt routing and scope composition aligned with per-order validation behavior. - Anti-forgery live diagnostics page: added an Ascorbic-only probe at
/Home/AntiForgeryProbewith both form POST and AJAX POST checks against a[ValidateAntiForgeryToken]endpoint (/Home/AntiForgeryProbePost) to verify live anti-forgery behavior directly. - Pending confirmation contract-match dedupe fix:
HomeController.ValidateOrderandValidateAllOrdersnow normalize merged corp+character contract feeds to uniquecontract_idrows and match onlyoutstandingcontracts before issuer/value comparison. This prevents falseMultiple orders found with the same user and value.results when the same contract appears in both ESI sources or when older non-outstanding contracts share the same amount. - Anti-forgery rollout to high-use account/order actions: added explicit
[HttpPost]+[ValidateAntiForgeryToken]protection toOnVacation,ValidateOrder,ValidateAllOrders, andMergeUserPointsToMain; updated Dashboard/User Management AJAX calls (OnVacation,SaveGateway,Validate,Verify All,Merge Alt Points) to send__RequestVerificationTokenin both headers and form body for proxy-safe validation.
Changes
- Task processor performance tuning: optimized
TaskProcessor.Bountieswith no-tracking read queries and lean corp/director projections, and optimizedTaskProcessor.RefreshUserInfoby skipping empty-user affiliation API calls, collapsing duplicate per-server saves into one conditional save, replacing per-user corp-id DB lookups with in-memory corp/user maps, batching Discord auth-role lookups per server, and skipping empty role-affiliation API calls. - Refresh-user affiliation lookup caching:
TaskProcessor.RefreshUserInfonow caches character affiliation results per refresh run and reuses them across server/member-role passes, reducing duplicateTryGetBulkCorpAffiliationAsync(...)calls for repeated characters in the same cycle. - Debug log-noise reduction: added a development logging override for
Microsoft.EntityFrameworkCore.ChangeTrackingatWarning, suppressing repetitive EF Core tracking debug messages (for exampleContext ... started tracking 'AuditLog' entity) from local debug output. - Mining-op alt mining-scope remediation: missing mining-scope ledger warnings now surface an actionable toast that names the affected alt and includes a direct re-authorize link, and the
MiningOpScopecallback now accepts re-auth for already-linked alts so the missingesi-industry.read_character_mining.v1scope can be refreshed instead of being rejected as a duplicate alt add. - Mining-op startup warning push: operation-launch baseline ledger initialization now forwards warning messages to op members immediately through SignalR
hasError(instead of only writing server logs), so missing/invalid alt mining scope issues are visible right after launch and can be fixed before the next sync cycle. ManualJoinOpnow pushes the same warnings immediately. - Mining-op on-screen auth warning panel: mining-scope alt failures now also render as a persistent warning list inside the active Mining Command Deck (not just toastr), now with clear per-alt
Re-authorize altandDismissbutton actions so scope re-auth and acknowledgment are obvious and clickable. - Mining-op miner-contributions alt health expander: each main miner card now includes an expandable alt section that shows only that main’s linked active-op alts with explicit health state. Successful ESI responses (including valid empty-ledger returns) show as
Healthy; failed syncs are marked orange with per-altRefresh ESIaction buttons. - Toast readability + size guardrails:
toastr8messages now wrap cleanly with no message-area scrollbars and are clamped to a fixed multi-line max height, preventing oversized toast boxes on long errors. - Contract-validation scope re-auth loop hardening: contract-scope authorization links now carry the requesting user id in OAuth state, and the
CorpContractsScopecallback now verifies the authorized character matches that requesting user. Wrong-character authorizations now return a clear expected-character error instead of silently updating another user and looping back toScope Required. - Contract-validation auth return routing: order-verification
Authorize ScopeandRelog Nowprompts now return toHome/OpConfirmationsafter auth/login instead of redirecting toHome/Dashboard. - Milestone roll-up repair + nested-alt support: milestone roll-up now walks the full linked-character tree, including nested alt chains created by main swaps. Toggling milestone roll-up now repairs and rebuilds milestone progress rows for the whole family to clear stale or duplicate progress data, and roll-up failures return a clear milestone-specific error instead of a vague generic toast.
- Milestone setup error clarity + logging hardening: create/toggle/delete actions in
Milestone Setupnow show the real server-provided error (including anti-forgery/session failures) instead of only generic toasts, andCreateMilestonenow performs explicit input validation and returns/logs clear database/runtime failure context when creation fails. - User Management publish-style resilience: added a scoped fallback stylesheet directly in
Views/Home/_UserManagement.cshtmlfor the redesignedum2-*layout so the page remains fully styled even if a publish target has a stale or missingContent/views-consolidated.css. - User Management milestone roll-up toggle request hardening: the roll-up toggle now includes a local anti-forgery token scope and sends explicit anti-forgery headers/context on
/Home/SetMilestoneRollupToMain/requests, plus safer client JSON error parsing. This prevents generic toggle failures when stale cached/global script state does not inject anti-forgery headers automatically. - Milestone roll-up toggle no longer depends on milestone setup/repair success:
SetMilestoneRollupToMain(...)now saves the roll-up setting first, then runs milestone-definition checks and family progress repair as best-effort follow-up steps with isolated logging. Users can now enable/disable roll-up even when no milestones exist, or when milestone repair fails temporarily. - User Management milestone roll-up anti-forgery transport fallback: the roll-up toggle now sends
__RequestVerificationTokenin both header and POST form body. This avoids persistent 400 request-validation failures in environments where underscore-prefixed custom headers are stripped by proxies/load balancers. - User Management milestone roll-up live hotfix: anti-forgery validation was disabled specifically on
SetMilestoneRollupToMain(...)to bypass live-only request-validation failures while keeping authenticated-user checks, and the client now appends HTTP status details when no JSON error payload is returned. - Contract-validation re-auth loop guard: contract
403errors are now classified more precisely. Scope re-auth prompts appear only for explicit missing-scope/token-scope failures, while role-related403responses (for example missing Director/CEO role) now return a direct role-permission error instead of repeatedly looping users throughAuthorize Scope.
Fixes
- Pending confirmations bulk-verify no-op fix: top-level
Confirm All/Verify Allactions now render only for users withConfirmOperationspermission, and theValidateAllOrdersclient handler now validates response shape before processing so unauthorized/non-JSON responses and empty pending-order sets return clear toast feedback instead of appearing to do nothing. - Build recursion/MSB3030 fix: excluded
EveHumanResources.Tests\**\*from web-project content discovery so nested test output folders are no longer copied as website content, preventing recursiveartifacts\publish\...copy paths and the missing-fileMSB3030failure during build. - Scope-required dialog button polish: cleaned up confirmation-dialog button rendering by overriding jQuery UI focus artifacts, keeping button labels centered and no-wrap, and adding responsive wrap behavior for smaller screens so
Authorize Scope/Laterlook and behave consistently. - Pending confirmation count mismatch fix:
ValidateAllOrdersnow applies the sameIsMiningOpReadyForConfirmationreadiness filter used by the pending-confirmation list views, soVerify Allno longer reports failures for hidden not-ready operations. - Verify All loading-state spinner: the
Verify Allaction now shows an inline spinner +Verifying...label, disables the button witharia-busywhile the request runs, and restores the original button state on completion. - Login callback latency improvement: login callback handling no longer waits on per-alt token refresh checks. Alt token refresh is now queued to a scoped background task so users can complete login without waiting for every linked alt token refresh call.
- New-corp bootstrap moved off login critical path: first-time corp setup records (
PricingTypeMappings,SystemSettings,UserPoints,LogisticSettings) are now initialized by a scoped background bootstrap task instead of blocking callback login, and newly created corps are redirected directly toSetupWizardafter sign-in. - Login callback side-effect offload: additional non-critical callback work now runs in deferred background processing after sign-in, including profile/name sync, corporation metadata lookup/enrichment, old-corp permission and Discord member-role cleanup on corp changes, and recruitment application side-effects (
CorpApplicationcreation, answer ownership updates, and broadcast). - Mining-op ledger refresh reliability: mining-ledger ESI calls now use a bounded configurable timeout (
AppSettings:MiningOpLedgerRequestTimeoutSeconds, default 45s) instead of an infinite wait, and scheduler failures in mining-op update processing now requeue active ops for a near-term retry (2 minutes) instead of waiting a full normal cycle after an error. - Mining-op scheduler tuning for 4-vCPU hosts: reduced mining-op scheduler parallelism from
3to2and mining-op finalization parallelism from2to1to reduce CPU burst spikes while keeping active-op refresh processing running. - Mining-op start latency reduction: clicking
Launch Operationno longer waits for synchronous main/alt baseline mining-ledger loads before returning. Initial baseline ledger loading now runs in a background task so the running-op page can render immediately while baseline sync completes. - Mining-op linked-ledger fetch pacing: active mining-op refresh now prefetches main and alt ledger API calls in parallel during member updates (configurable with
AppSettings:MiningOpLedgerFetchParallelism, default4), removing per-user sequential API-call gaps inside each refresh pass. - Mining-op UTC time display: standardized mining-op timestamp rendering across active-op, current-ops, pending confirmations, op history, and unconfirmed-op member rows to explicit UTC game time labels (for example
HH:mm UTC) using UTC-safe conversion for unspecified DateTime kinds, eliminating local-time drift on the page. - Mining-op linked-ledger fetch strategy update: removed linked-user prefetch parallelism from mining-op update processing, so ledger API calls now execute sequentially per member with no intentional delay between calls. Removed the now-unused
AppSettings:MiningOpLedgerFetchParallelismsetting from appsettings files. - Mining-op sync refresh fallback + SignalR sync token fix: corrected SignalR mining-op sync-token reads to use the current
.MiningOpTime[data-lastsync]banner attribute (with legacy markup fallback), and added an overdue-cycle fallback refresh so once the countdown reachesawaiting sync..., the client periodically requestsGetJoinedOp()until a fresh sync arrives even if push delivery is delayed or missed. - Mining-op non-threaded
UpdateOpperformance pass: kept mining-ledger API calls sequential and always fetched fresh ESI ledger data per linked-user check, while retaining local non-ESI caches for per-item sale-price resolution and item-group lookups used by ore filtering. This preserves freshness while still reducing duplicate local compute.
Fixes
- Google ads + consent flow hardening: added explicit Funding Choices loader before AdSense script load, parameterized publisher client ID via app settings, and normalized ad slot/client usage to one canonical source.
- Google consent persistence improvements: consent hook now caches/replays last consent mode values in local storage, ingests Funding Choices consent mode values when available, and deduplicates consent-update calls to reduce repeated consent churn (especially on mobile).
- Patron ad suppression hardening: ad vendor script loading now respects patron checks consistently (including Infolinks gating),
AdBlocker()always returns non-block for patrons, and patron resolution now has DB-backed fallback refresh to avoid stale auth snapshots showing ads for patron corps. - AdSense-only runtime + live-cycle ad refresh: removed remaining Infolinks script injection, retired the unused Infolinks app-setting toggle, added reusable ad-slot render helper for AJAX partials, and wired live Mining Op/Fleet panel ad slot re-render after cycle refreshes.
- Patron ad eligibility claim-drift fix: patron checks now validate live user-to-corp mapping when claim corp data is stale and refresh auth snapshots from the resolved corp, preventing ads from showing to patron-corp users with outdated claims.
- Validation scope prompt modal: pending-order
Validate/Verify Allnow returns explicitScopeRequiredresponses and shows an authorize modal action instead of hard redirects or raw ESI errors. - Pending-order validation 500 hardening: fortified validation paths against null scopes, missing issuers, and contract-item scope failures with non-throwing logging, so failures return stable JSON responses instead of internal server errors.
- Pending-order validation UX hardening: replaced raw ESI endpoint failure text in user-facing toasts with friendly contract-validation guidance (expired login, missing scope, temporary ESI outage, delayed contract visibility) while keeping raw details in server logs.
- Contract validation scope policy update: pending-order validation now requires both corp and character contract scopes for validating directors/CEOs, and re-auth prompts now request both scopes together.
- Mining-op final reconciliation gating: ended ops now hold for a reconciliation delay window before final ledger+payout processing, and pending/confirm flows require post-delay reconciliation completion to prevent early confirmations on pre-final totals.
- Mining-op missing-ore miner alert: mining-op member UI now shows a visible warning banner when unmapped ore rows are detected, including ore names/type IDs and guidance to contact Ascorbic for mapping correction.
- Mining-op finalization durability + auto-end parity: removed non-durable finalization task dispatch from manual end flow, moved final reconciliation to scheduler-driven retryable processing, and aligned auto-ended member closeout behavior with manual-end finalization pipeline.
- Mining-op payout undercount fix for linked miners/alts: payout aggregation now resets existing history amounts before recompute and sums deltas by
(UserId, MarketId), preventing main+alt same-ore rows from overwriting each other and undercounting totals. - Mining-op leave final-sync wait fix: leave-op flow is now async and correctly awaits remaining API cache-expiry time before the final member ledger refresh.
- Mining-op new-ore baseline update: newly observed ore rows now initialize with
PriorAmount=0so first appearance contributes immediately to op totals. - Mining-op regression tests: added tests covering payout delta aggregation, final-reconciliation delay/threshold policy, and first-seen ledger row initialization to guard against regressions.
Changes
- zKill ingestion API migration: moved from RedisQ polling to the R2Z2 sequence API and updated listener parsing to consume sequence payloads with embedded ESI killmail and ZKB metadata.
- zKill listener behavior tuning for R2Z2: added explicit handling for R2Z2 polling semantics (404 no-new-mail wait, 429 retry/backoff with
Retry-After, preserved 403 cooldown handling) and sequence iteration updates per processed payload. - zKill config update: websocket bootstrap now passes R2Z2 base URL and polling delay settings from app config, with defaults added to standard and development appsettings.
- Recruitment Applications page refresh: rebuilt the applications view into a cleaner tabular review layout, restored per-application status dropdowns, and added resilient portrait/name fallbacks for missing user records.
- Recruitment application-detail status restore: restored the status dropdown control on the application detail page so reviewers can update status inline again.
- Recruitment status-change handler hardening: status change JavaScript now accepts both
dataanddata-ididentifiers and restores previous selection if update requests fail. - Discord owner reset command: added owner-only Discord-link reset support via both slash and text commands, with a reset service that removes guild-scoped Eve-HR Discord linkage records so setup can be cleanly re-enabled.
- PI alt refresh reliability + stale fix: PI refresh eligibility now requires usable PI auth state, linked alt refresh cadence is accelerated when mains open PI, background sync excludes revoked/empty-token users, and stale-for-days scope gating from unrelated scope-invalid flags was removed so PI refreshes depend only on PI scope plus token/revocation state.
Fixes
- Google Consent Mode v2 implementation (UK/EEA/CH): replaced the temporary country-block approach with proper consent defaults in shared layout and landing page using
gtag('consent','default', ...)forad_storage,analytics_storage,ad_user_data, andad_personalization(granted baseline plus denied regional override withwait_for_update), so ads can still run while consent state is respected. - Google tag/config hardening for consent flow: moved Google tag bootstrap earlier in page render (before ad calls), made Analytics tag ID and consent wait configurable via app settings (
GoogleAnalyticsTagId,GoogleConsentWaitForUpdateMs), and added an app-setting toggle for Infolinks loading (EnableInfolinksAds). - Patreon button label typo: corrected the topbar CTA text in shared layout from
PATRONtoPATREONso branding matches the destination link. - PI stale-status refresh fix: corrected background PI sync scope filtering to use
esi-planets.manage_planets.v1, added pin-cache staleness checks so outdated pin rows are force-synced even when planet cache rows appear fresh, and added fast retry scheduling when planet list refresh succeeds but per-planet pin detail refresh fails. - Ad-blocker detection + support prompt: added a polite, dismissible ad-block notice for non-patron users in shared layout that asks users to whitelist
eve-hr.comor support via Patreon when ad blocking is detected. Detection uses a bait-node probe plus ad-slot/script fallback checks, with local throttling (once/day display, 30-day dismiss) and existing server-side once-per-day gating viaHomeController.AdBlocker. - Mining Op join first-click reliability: removed client-side 45-second timeouts from the join transition (
JoinOprequest +GetJoinedOprefresh) and hardenedGetJoinedOpoption/callback queueing so overlapping refreshes cannot drop the join completion callback or downgrade active-op retries, preventing cases where users had to refresh before join state appeared. - jQuery hardening pass (security + reliability): refactored shared jQuery helpers in
EHR.jsto render error alerts safely (no raw HTML injection), handle close actions without log IDs, and resolve anti-forgery tokens more reliably by preferring the layout-scoped global token plus scoped fallbacks while respecting request-level token headers. Updated SignalR error wiring to pass structured payloads (message + optional id) into the safe renderer. Hardened mining-op refresh error handling inDashboard.jswith retry-awareGetJoinedOpbehavior and fixed countdown drift by recalculating current time each interval tick. - zKill RedisQ 403 cooldown handling: updated
ZKillListenersoHTTP 403 Forbiddenresponses now trigger a listener-wide cooldown (ForbiddenRetryPause, default 1 minute) before additional polls are attempted, reducing repeated forbidden spam and backing off request pressure when zKill throttles RedisQ queue listeners. - Exception detail propagation for diagnostics: added shared exception-chain formatting in
LogsoLog.Error(Exception,...)records nested inner exception messages/stacks by default, updated process-capture error propagation and mining-op member save warnings, added exception-aware JSON error helpers for controller catch handlers, and updated the global exception filter development payload to include inner-exception chains. Surfaced warning/JSON error details now include actionable root-cause context instead of only top-level exception text. - Mining-op join flow hardening and invite link routing: Discord mining-op announcements now post the direct per-op invite URL (
/MiningOp/{code}) as the primary join link (with Current Operations as backup). Server join handling now validates active-op/corp access on/Home/JoinOp, treats repeated same-op joins as idempotent success, blocks second active-op joins with a clear message, and returns proper error-page output for invite-link join failures. Scheduler next-update anchoring was also normalized to a true 10-minute cadence so first-cycle and subsequent API refresh timing aligns with expected mining-op checks.
Fixes
- Mining Op create double-submit guard: hardened create-op flow in both UI and server so rapid repeated clicks can no longer create multiple operations.
_MiningOpStartnow sets in-flight state and disables the launch button immediately on click, andHomeController.MiningOpCreationnow enforces a server-side active-op guard inside a creation lock before insert. - Slot Machine cross-corp spin availability: switched slot prize-code availability and spin code draw to a shared site-wide inventory pool instead of per-corp checks, so all corps can spin whenever codes exist. Code uploads remain restricted to
Ascorbiconly, with global duplicate-code protection and updated status/admin messaging to match.
Changed
- Price Control load performance: optimized
Setup/SetPricesto use no-tracking reads, removed write-on-read mapping inserts during page load, and now synthesize missing pricing-system mappings in-memory for UI defaults so opening Price Control no longer performs DB writes. - Price Control first-render latency: preloaded the first calculator's formula rows into the initial pricing partial render (`_SetPrices` + `_PriceCreator`) and removed the extra first-load formula fetch request, reducing initial request churn.
- Price Rules item-name recovery:
GetPriceRulesnow resolves missing names via ESI universe item lookup when a ruleMarketIdis absent from localItems, and caches recovered rows intoItemsfor future local reuse. - Item autocomplete API mode:
Home/ItemNameSearchnow uses ESI inventory-type search (strict=false) as the primary suggestion source, resolves type IDs to names, and caches both prefix suggestions and newly discoveredItemsrows locally for faster follow-up lookups. - Item autocomplete ore-variant completion:
Home/ItemNameSearchnow adds targeted grade probes (I/II/III-Grade) and always merges localOresprefix/contains matches, so ore variants likeScordite III-Gradeand compressed ore names remain visible in suggestions.
Fixes
- Price Control partial-reload handler stacking: calculator list click binding now uses namespaced
off(...).on(...)rebinding in_PriceCreator, preventing duplicate click handlers after repeated partial reloads. - Price Rules save lookup fallback:
AddPriceRulenow falls back to ESI inventory-type search when a typed item is missing from localItems, then caches the resolved item locally before saving so missing ore grade entries can still be added.
Fixes
- Startup reliability hardening (500.30): startup now avoids DB-dependent MySQL version auto-detect by default and runs migration/schema initialization inside a configurable retry loop, preventing transient post-reboot DB readiness delays from immediately crashing app boot.
- Mining op join infinite-loading fix: hardened dashboard mining-op refresh retries so the loading overlay is only applied once across retry cycles (preventing stacked overlays), plus added request timeouts and clearer retry-exhausted feedback when an active-op refresh cannot be loaded.
- Mining op sync latency reduction: scheduler mining-op updates now cache mining-ledger API responses per linked character for the full sync cycle (so repeated linked accounts are not re-fetched) and use a fail-fast 10-second ledger request timeout, reducing multi-minute syncing stalls when ESI calls hang.
- Mining op ledger timeout alignment: removed mining-op ledger dependency on the default 30-second HTTP timeout by applying the mining-op timeout override across all mining-op ledger update fetch paths (scheduler cached fetch, initial Start Ledger pull, and update fallback fetch), preventing 30-second-per-call waits during op sync.
- Mining op API caching removal: removed in-process mining-op ledger response caching from the op update loop, so mining-op sync now relies on ESI’s own cache behavior instead of app-side response reuse.
- Mining op timeout removal: mining-op ledger update fetches now use no app-side HTTP timeout cap (`Timeout.InfiniteTimeSpan`) so op sync calls are no longer cut off by local timeout limits.
- Mining-op history filtering fix: buyback submissions (`OpType = Personal`) are now excluded from mining-op history/totals/personal history query paths, so buyback orders no longer show up in mining operation history pages.
- Mining-op history separation flag: added persisted
IsBuybackOrderon mining ops (with migration backfill for existing Personal rows), set the flag when buyback orders are created, and updated mining history/totals filters to exclude flagged buyback rows so future buyback submissions stay in buyback flows instead of mining-op history pages. - Price Rules save fix (Price Control): added a page-scoped anti-forgery token for the Price Rules tab and now post that token in both header and form data on Add/Delete rule actions, fixing generic
Error saving rule.failures when adding price controls.
Changes
- User guide expansion (Price Controls): added a screenshot-first Price Controls walkthrough to
USER_GUIDE.mdwith captured UI references for formula setup, step configuration, pricing-system mapping, and ore-value validation, plus new screenshot assets underdocs/screenshots/price-controls/. - In-app guide conversion: replaced the old video-links-only
Tutorialspage with a full on-site HTML guide section that documents major site modules and embeds Price Controls screenshots served fromwwwroot/images/guides/price-controls/.
Fixes
- Lucky Slot Machine 400 fix: slot-machine save/spin POSTs now use a page-scoped anti-forgery token source and include
__RequestVerificationTokenin form data (as well as headers), preventing cross-form token mismatches that could return400 Bad RequestonAddSlotMachineCodes/SpinSlotMachine.
Fixes
- Setup Wizard scope save 400 fix: Step 1 now always includes the anti-forgery token in form POST data and posts
selectedScopeswith traditional array serialization, andSaveSetupWizardScopesnow binds from a nullable form list for more reliable scope payload handling. - Setup Wizard all-step anti-forgery fix: wizard saves/completion now use a dedicated wizard-scoped anti-forgery token source instead of a global token lookup, preventing cross-form token mismatches that could cause every wizard POST to return
400 Bad Request. - Setup Wizard completion 400 fix: final
/Setup/CompleteSetupWizardnow posts anti-forgery token in form data as well as headers (matching step-save behavior), preventing header-only token validation failures on finish. - Setup Wizard Step 4 save speed-up: refining save now uses bulk mapping loads and in-memory updates (instead of per-ore mapping queries), projects only required ore fields, removes an extra intermediate save, and precomputes refine multipliers to cut Step 4 save latency.
- Setup Wizard Step 4 speed-up follow-up: added short-lived ESI ore-skill caching for per-ore mode, skip full mapping recompute when refining inputs are unchanged (while still healing missing mappings), and reduced change-tracker overhead during mapping updates.
Fixes
- Discord setup save reliability: corrected Discord role/channel sync cleanup in
DiscordController.DiscordSetup()to stay scoped to the activeDiscordServerId, fixed a role-prune loop issue, and added a safe fallback to render persisted DB values when live guild sync is unavailable. - Discord settings endpoint hardening:
DiscordController.SaveRoleis now explicitPOSTwith anti-forgery validation, and the Discord settings form now posts with an anti-forgery token. - Setup Wizard Discord channel save reliability: switched Discord channel ID fields to precision-safe text entry and added server-side trim/validation/parsing in
SaveSetupWizardDiscordso large Discord snowflake IDs save reliably. - Setup Wizard save hardening: pricing/refining/loyalty save endpoints now reject invalid model binding states, refining implant values are clamped to the valid
0.00-1.00range, and pricing step save now returns a clear error when no pricing formulas exist yet. - Recruitment Welcome save toast fix: welcome-page save now uses app-safe toast helpers (prefers
toastr8, falls back totoastr) so save callbacks no longer throwReferenceError: toastr is not defined. - Bulletins editor save toast fix: bulletin save now emits explicit success/error toasts (prefers
toastr8, falls back totoastr) soSave Bulletingives clear confirmation/error feedback. - Recruitment setup save toast fix: recruitment setup save now uses app-safe toast helpers (prefers
toastr8, falls back totoastr) so save callbacks no longer throwReferenceError: toastr is not defined. - Setup + Setup Wizard toast compatibility fix: setup-page and setup-wizard saves now use shared safe toast helpers (prefers
toastr8, falls back totoastr) so callbacks do not fail when only one toast global is available. - Discord setup save UX fix: Discord settings now save through AJAX with in-page toast feedback, so
Save Settingsno longer navigates to raw JSON at/Discord/SaveRole. - Discord setup save responsiveness: Discord settings save now shows a clear loading state (
Saving...+ spinner overlay) and server persistence now applies only changed notification rows instead of delete/reinsert each submit. - Discord Additional Corp Auth button fix: updated the
Add Corp To Discordaction to use scoped Discord button styling with a minimum width, preventing label clipping/truncation.
New Features
- Planetary Industry: new module showing all colonies across your main and alt characters in one view. Each planet card displays its status (Active / Expiring / Expired / Idle), extractor countdown timers, upgrade level, and pin-type chips (extractors, basic/advanced IFs, launchpads, etc.). Colony data is cached in the database and refreshed by the background worker every 30 minutes, with an immediate live ESI fetch on first visit.
- PI scope gate: if the
esi-planets.read_customs_offices.v1ESI scope is missing, the Planetary Industry page shows a clear prompt and a one-click re-authorisation link instead of an error.
Fixes
- PI refresh timing: Planetary Industry page loads now force an immediate live ESI refresh for the requesting character (no 30-minute wait), and also refresh linked characters when their PI cache is stale or missing.
- PI view crash fix: removed the duplicate-key assumption in the Planetary Industry aggregation view so multiple characters with colonies on the same planet no longer throw rendering exceptions.
- PI status timer fix: corrected extractor expiry rollup logic so colonies with extractor pins but no expiry timestamp no longer show false
Expired/0001-01-01-style next-timer output. - PI cache cleanup: background PI sync now removes deleted in-game colonies (and their cached pins) from local cache tables, preventing ghost colonies and stale alert state from lingering after refresh cycles.
- Legacy planets fallback safety: hardened the older
Home/GetPlanetspath with safe default PI objects when ESI calls fail so it no longer null-refs if that fallback UI is used.
Security Hardening
- Anti-forgery enforcement: added
[HttpPost]+[ValidateAntiForgeryToken]protection to milestone, slot-machine, gateway, and recruitment setup save endpoints; slot-machinefetchPOST calls now include the anti-forgery header. - Recruitment DOM-XSS fix: replaced unsafe HTML-string error rendering in
NewApplicationwith escaped/safe error box rendering before DOM injection. - Corporation lookup safety: removed raw corporation description rendering and now only outputs clickable corp URLs when they are valid absolute
http/httpslinks. - Claude API endpoint hardening: added constant-time API-key comparison, HTTPS/loopback transport checks, and optional caller IP allowlisting via
AppSettings:ClaudeApiAllowedIps. - Header + script hardening: added baseline security headers (including CSP report-only) and switched protocol-relative third-party script loading to explicit
https://.
Highlights
- Personal Mining History mockup: added a new standalone UI concept file,
eve-personal-mining-history-mockup-a.html, forHistory/PersonalMiningHistorywith EVE HR-style navigation, KPI strip, filter controls, and expandable operation rows. - Recruitment mail tab refresh: redesigned the application-view mail panel to use scoped, modern EVE HR styling (toolbar stats, cleaner row hierarchy, and responsive recipient/message split), and switched lazy-load selectors to the scoped
.js-mail-rowclass to avoid legacyMail/Read/UnReadstyle and click-handler collisions. - Milestone delete flow: added a new delete action on
Milestone Setupcards with confirmation and a matching server endpoint so Loyalty Setup admins can fully remove milestones (including associated progress rows), not just disable them. - Recruitment tab consistency pass: refreshed
JournalandNotificationstab panels to align with the updated mail-tab styling (scoped panel shell, compact summary toolbar, cleaner row spacing, and responsive behavior), and switched notification read-state styling to scopedis-read/is-unreadclasses to prevent legacy style bleed. - Setup Wizard context pass: added inline context/help text across wizard selections (scopes, system toggles, pricing mappings, refining options, loyalty controls, recruitment fields, and Discord channels/messages) so each option clearly explains behavior and impact before saving.
- UI/button collision hardening: replaced broad shared click handlers with scoped namespaced bindings across alliance, fleet, logistics, and mining-op setup panels to stop cross-page double-fires after partial refreshes.
- Duplicate toast reduction: disabled legacy global AJAX toast emission paths and tightened toast dedupe/rate-limiting so repeated button actions stop stacking success/error popups.
- System Settings save follow-up: moved
/Setup/SystemSettingssave to a single explicitfetch(...)path with in-flight locking to prevent a second hidden/globalSavedtoast from appearing on save. - Setup save follow-up: moved the main
/Setup/Setupform save (#SetupForm) to a namespaced, in-flight-guardedfetch(...)submit path so setup saves no longer emit duplicate success toasts after partial reloads. - High-risk duplicate toast cleanup: added namespaced event rebinding (plus in-flight guards where needed) for Fleet Up settings, Logistics settings, Setup User Management actions, Order submit/confirm/cancel actions, SRP action handlers, and Setup price-rules add/delete actions to stop stacked toasts after partial refreshes.
- Security hardening: stopped global exception stack-trace leakage, enforced anti-forgery on key state-changing endpoints (Home/Setup/Alliance/Bulletins/Recruitment), added shared anti-forgery token/header wiring in layout + core jQuery helpers, sanitized New Member Guide body/title save paths, and removed raw bulletin-title rendering to close stored-XSS vectors.
- Dynamic UI stability pass: replaced deprecated DOM mutation hooks with
MutationObserver-based handling and removed duplicate script/UI initialization paths that could rebind controls. - Layout/ID cleanup: fixed duplicate IDs in key partials and corrected trailing layout tag placement so dynamic partial rendering no longer destabilizes selectors or script execution order.
New Features
- Custom milestones: added a corp-scoped milestone system for
Ore Mined,Ships Killed, andBounty ISKwith persisted definitions, progress, completions, and event tracking. - Milestones member page: added
/Home/Milestonesso members can see active milestone progress, completion state, repeat counts, rewards, and earned trophies. - Milestone setup: added
/Home/MilestoneSetup(Loyalty Setup permission) with milestone creation, metric/window/filter controls, reward options, and enable/disable actions. - Random trophy icon selection: added randomized icon-pack generation with selectable milestone trophy icons (glyph + gradient + ring), persisted on each milestone.
Changed
- Automatic milestone progression: mining-ledger deltas, killmail attacker processing, and bounty ingestion now emit milestone events so milestones progress from existing corp activity without manual updates.
- Corp Members trophies: corp roster rows now display earned milestone trophy icons per member with hover tooltips showing milestone titles.
- Top-banner trophies: logged-in users now see earned milestone trophies in the top banner/nav user info (desktop and mobile), including repeat-completion counters.
- Loyalty navigation: added dashboard entries for
MilestonesandMilestone Setup.
Highlights
- Killmail processing hardening: fixed killmail LP award paths for null Discord channels, safer
awox/soloboolean coercion, and improved corp cache parsing resilience. - Killmail/bounty pipeline controls: added configurable zKill queue IDs, intake backpressure limits, and startup/task flow updates to improve queue stability.
- Setup Wizard polish + routing: improved first-login setup wizard copy/layout and fixed dashboard routing/guard behavior so eligible users are redirected cleanly until corp setup completion.
- Setup Wizard completion model: added persisted corp completion state with completion endpoint wiring and post-wizard return routing.
Bug Fixes
- UI alignment pass (Fleet/SRP/Fitting/Setup): fixed multiple input-and-button alignment issues across
SRP/SRP,Fleet/FleetTypes,Fleet/FleetHistory,Home/FittingHome,Home/MiningOpsDownload, andSetup/DirectorOnlyPageby replacing legacy inline layouts with responsive grid/flex control rows and consistent baseline alignment. - Button text overflow: updated affected pages to use responsive button sizing (
width: autowith sensible minimums and mobile full-width fallbacks) so labels no longer spill outside button boundaries. - Fitting import upload row: modernized
Views/Fitting/ReadFile.cshtmlfrom a legacy inline div-button row to a semantic file-input + button layout, including mobile stacking and a stable upload target lookup for the existing AJAX import flow. - Setup pricing-system assignment save: fixed the Save buttons in
Assign Calculators to Pricing Systemsfailing when no Market Hub dropdown is rendered in the row. The client now posts a safe default hub (Jita) when the control is missing, andSetupController.SetPriceTypenow accepts nullable market-hub input and defaults server-side toJita. - Setup pricing-system dropdown preload: fixed saved calculator selections not reliably showing on page load when duplicate
PricingTypeMappingsrows existed.SetPricesnow loads the newest mapping per pricing system,SetPriceTypeupdates the newest row and removes older duplicates, and setup bootstrap no longer creates repeatedPricingSystem.Nonerows. - Setup pricing-system save authorization/binding: aligned
SetPriceTypeauthorization with the Prices page (BuybackSetup), switched assignment-rowdata-idvalues to numeric enums, and enforced JSON parsing for the Save AJAX response. This prevents false-positive saves on auth redirects and makesSale/Logistics/SRPenum binding deterministic. - Setup pricing-system dropdown selected state: fixed assignment dropdowns rendering the first/default calculator on reload by replacing complex dictionary-object dropdown binding with explicit
<select>option rendering keyed to each system’s savedPricingTypeId. Saved selections now render correctly per system when the page loads. - Mining Prices refresh + mineral-pricing toggle: fixed
Home/GetPricePerM3values appearing stuck after changing pricing controls or toggling Mineral Pricing. The page now respectsSystemSettings.MineralPricing(direct ore sale pricing when off, refined-mineral valuation when on), uses mode-specific cache keys, and setup/pricing save endpoints now invalidate Mining Prices cache immediately after save. - Ore pricing formula hover details: added per-ore hover formulas with real numbers on both
Mining Pricesand liveMining Opore breakdown rows. Tooltips now show how each ore value was computed (including refined-mineral component math when mineral pricing is enabled, or direct unit-price-per-m3 math when it is disabled). - Ore valuation formula consistency: corrected ore refined-value math so
Mining Prices, ore hover formulas, and live mining-op ore totals all use the same per-ore component equation (floor(refine amount × refine %) × mineral price / amount to refine, then/ volumefor m3 views). Also updated mining-ledger refresh paths to recalculate stored ore unit price on sync so ore values stay aligned with current pricing settings. - Mining Op end-operation UX for all members: ending an op now sets a shared transient ending state so everyone currently in that op sees an in-panel
Ending Operation...spinner overlay during live polling (not just the user who clicked End). End buttons are also locked while the request is in flight, and the client fast-polls op state until completion. - Mining Op end-operation background processing + immediate removal:
End Opnow marks the op inactive immediately, returns a clear background-processing message, and pushes a SignalRupdateFinishOprefresh right away so the live op panel disappears for all members and becomes non-interactable while final ledger refresh/payout work continues in a background task. Confirmation availability now follows when that background finalization completes. - Mining Op pricing mode enforcement: mining-op value panels now re-resolve live ledger ore prices through the sale pricing workflow on each refresh (banner totals, ore breakdown, and member payout panel). With
SystemSettings.MineralPricing=true, mining ops now always value ore using refined-mineral pricing instead of stale/raw ore market pricing. - Mining Op create-flow spinner continuity: fixed the create-op transition so the loading spinner now remains visible until the joined active-op panel is actually returned. Added active-op-aware retry behavior to
GetJoinedOp()and switched create success from immediateUpdatePage()to spinner-backed active-op polling, eliminating the briefNo active mining operations right now.flash. - Mining Op create/join transition loading gap: extended active-op retry windows for create/join flows so spinner states stay active while backend join/start processing completes, and updated initial
/Mining/MiningOpspage load to use spinner-backedGetJoinedOp(...)with active-op retry when the server detects the user is already in an active op. - Mining Op scheduler latency reduction: reduced backend sync jitter by moving mining-op scheduler checks from roughly 1-minute cadence to a 10-second due-op cadence (with a 5-second worker heartbeat), and now querying only due ops (
NextUpdate <= now) for sync processing. This narrows the countdown-to-actual-sync gap. - Mining Op member panel flicker: fixed miner contribution cards briefly disappearing/reappearing during live op polling by carrying forward the previous members HTML until the fresh members partial returns.
- Mining Op mineral-pricing parity + indicator: when mineral pricing is enabled, op-level gross/to-miners/tax values now align to the Refined Values total, member payouts are scaled to the same refined gross basis, and the banner now shows a
Mineral Pricingmode chip so valuation mode is explicit. - Mining Op push refresh via SignalR: replaced mining-op sync refresh dependence on countdown fast-polling with server push updates. Added an ASP.NET Core SignalR client on the dashboard, broadcast mining-op update events (sync/join/leave/end/create/manual refresh/auto-end), and changed countdown fast-poll to fallback-only when SignalR is disconnected.
- Mining Op inactivity auto-end (never-active ops): updated scheduler inactivity logic so ops are auto-ended after 1 hour with no activity even if they never had a successful sync (
LastLedgerUpdateis null). The inactivity clock now usesLastActivityTime, thenLastLedgerUpdate, thenOpStartTime. - Mining Op per-character ore attribution tab: added a new
My Orestab in the live op centre panel that shows the current viewer’s mined ores split by mining character (main + linked alts), including per-character totals and per-ore value rows. AddedMiningLedgerItems.SourceUserId(migration20260315000005_20260315_MiningLedgerSourceUserAttribution) and updated ledger start/sync writes to store source-character attribution for new ops, while keeping legacy ops compatible with an attribution note. - Mining Op member payout panel flicker/blanking: hardened the member refresh path to prevent overlapping
GetMiningOpMemberscalls from clobbering the panel during rapid polling/push refresh cycles. Member updates are now serialized with queued coalescing, non-HTML/JSON responses no longer replace member HTML, andUpdatePage()no longer triggers a redundant second member fetch on top ofGetJoinedOp(). - Mining Op sync-delivery timing gap: fixed the post-countdown delay where the timer could restart a new cycle before refreshed ledger values appeared. The mining-op banner now carries a machine-readable
last synctoken, due-sync polling now waits for both a futureNextUpdateand a changedlast syncmarker before declaring the cycle refreshed, andGetJoinedOp()calls are serialized with queueing so overlapping refreshes cannot race and present stale data as a fresh cycle. - User Management load performance: sped up
/Home/UserManagementinitial render by returning a lightweight shell and loading the character section asynchronously through/UserControls/GetCharacters. Also reduced DB overhead in both actions by replacing heavyGetUser(...)paths with directAsNoTrackinglookups, avoiding unnecessaryAltsnavigation loading on first paint. - Mining Ledger load performance: optimized
/Mining/GetMiningLedgerwith short-lived ESI ledger caching per character, a single-pass aggregation pipeline, direct invoice-item projection via join query, and controller-side precomputation of ore names/prices/totals._Ledger.cshtmlnow renders precomputed values instead of callingGetPrice/GetNameper row, and theGet Ledgerbutton now blocks repeat clicks while requests are in flight. - Mining Ledger duplicate key crash: fixed
An item with the same key has already been addedon/Mining/GetMiningLedgerby making item-name dictionary creation duplicate-safe. Item names are now grouped bytype_idbefore dictionary materialization, so corp datasets with duplicateItems.type_idrows no longer throw. - Mining Ledger and invoice pricing batch lookup: replaced per-item sale-price calls in
MiningControllerwith batchedGetSalePrices(...)lookups for both ledger render and invoice creation paths, reducing repeated pricing pipeline setup/queries per row and improving responsiveness for larger ledgers. - Order Confirmations Block action: fixed the
Blockbutton click handler throwingReferenceError: e is not definedinDashboard.js, which could break confirmations-page interactions. The handler now correctly receives the event parameter, unblocks UI on errors, and avoids duplicate cancel-dialog invocation. - Order Confirmations save failure: fixed payout confirmation writes that could fail with EF's generic
An error occurred while saving the entity changesmessage by ensuringPayoutHistory.UserIdis always populated fromMiningOpMember.UserId(even when theUsernavigation is not loaded) and by hardening fleet payout math against missing fleet setup / zero tick totals that could produce invalid payout values. - Order Confirmations loyalty points award: fixed confirmed ops not awarding loyalty points from miner tax amounts.
ConfirmOp/ConfirmAllOpnow call the context-awareTallyPointsoverload so point updates are tracked in the same request context and persisted with confirmation saves. Also hardened standaloneCoreWorkflowService.TallyPoints(...)to save changes when it creates its own context. - Task scheduler mining-op speed pass: optimized
TaskProcessor.Schedulerwith a fast due-op ID query to skip heavy include-graph loading when no ops are due, switched due-op loading to split query, resolved payout workflow scope once per scheduler run instead of once per op, and persisted allNextUpdateanchors in oneSaveChanges()before sync/payout work. Also reducedAutoEndInactiveOpsscan cost by prefiltering to active ops older than 1 hour and reusing a single UTC timestamp in-loop. - Buyback refining calculator settings: replaced the old base-yield-only refine setup with calculator-style settings on
Setup/Setup(Structure, Rig, Security Status, Reprocessing Skill, Reprocessing Efficiency, Ore Processing Skill, Implant), persisted those values inOreRefiningPercentages, and switched refine-percentage generation to the EVE-style equation so saved setup values now directly driveOreRefiningMappingsused by mineral pricing. - Buyback ore-processing mode choice: added a corp-selectable
Ore Processing Modeso each corp can choose eitherGlobal LevelorPer Ore (ESI). Refine mapping generation now follows that selected mode and persists it inOreRefiningPercentages.
New Features
- Mining Op — live next sync countdown: replaced the static "Next sync 9:15 AM" text in the op meta bar and Sync Health card with a live client-side countdown (
5m 42s → syncing...) so users always see exactly how long until the next ledger refresh ticks over. - Mining Op — refined values tab: added a new
Refined Valuestab beside Ore Breakdown/leaderboards that calculates refined outputs using the configured setup refining percentages and ore-to-mineral mappings. The tab now combines all refined outputs into a single per-mineral summary (amount and ISK value per mineral), shows a combined refined total, and includes a clear hint when some ores are missing refine setup mappings.
Bug Fixes
- Mining Op invite links — auth redirect and join reliability: fixed the
MiningOp/{code}invite OAuth callback flow so successful auth returns users to the invite route, repeat clicks on the same invite no longer fail, and users already active in a different op still get the correct "leave current op" guard. Also hardened callback corp/op checks to avoid null failures during invite auth. - Mining Op join button — spam click protection and loading UX: hardened the join click path so repeated clicks no longer fire duplicate join requests. Join buttons now lock while the request is in flight, show an inline
Joining...spinner state, and then load the joined op panel via a spinner-backed refresh so users see a clear loading transition into the op screen. - Mining Op refined minerals — zero ISK values: fixed the new refined-values tab showing
0.00 ISKfor minerals by switching valuation to the same live sale-pricing workflow used by ore pricing (GetSalePriceswith shortcut fallback), instead of relying only on directMineralPricestable reads. - Mining Op payout consistency and ISK/hr: fixed member-side ISK/hr and payout display drift by normalizing member start times with UTC-safe conversion (so
DateTimeKind.Unspecifiedvalues no longer inflate rates) and by calculating member estimated payout from the same live ledger + tax/fleet formula used by the op banner, keepingTo Minersand pilot payout aligned for single-pilot ops. - Mining Op tab persistence during polling: fixed automatic op refresh from constantly snapping the centre panel back to
Ore Breakdown. The selected tab is now remembered and restored after eachGetJoinedOp()partial refresh so users can stay onRefined Values,Top Miners, orISK / hrwhile polling continues. - Ore names — startup auto-refresh from ESI: added startup synchronization that fetches each ore
Type_Idfrom ESI and updatesOres.OreNameautomatically, so CCP ore renames no longer require manual mapping maintenance. - Ore name startup sync control: added
AppSettings:EnableStartupOreNameRefreshso this startup refresh only runs when explicitly enabled in configuration (defaultfalse). - Mineral mapping normalization: added migration
20260314_NormalizeMineralOreMappingMineralIdsto normalizeMineralOreMappings.MineralIdfrom legacy EVE type IDs to trueMinerals.MineralIdforeign keys. Updated ore pricing paths to resolve market type IDs throughMinerals.EveCentralId(with legacy fallback) so Veldspar and other ore mineral lookups remain correct. - Mining Op — next sync time accuracy:
NextUpdatewas previously stamped after the sync completed, so the displayed time reflected "10 min from when processing finished" rather than from when it started. The scheduler now savesNextUpdatebefore callingTryUpdateOp, so the correct upcoming time is visible immediately — even while a slow multi-member sync is still running.
Bug Fixes (earlier today)
- Fleet settings save button — full-page navigation: fixed "Save Fleet Settings" navigating to a raw
/Fleet/GetSettingspage instead of saving in-place.Html.BeginFormwas generating the wrong action URL when the partial was loaded via AJAX, and the submit handler was unreliable inside injected partial content. ReplacedHtml.BeginFormwith an explicit<form action="/Fleet/SaveSettings">tag, added[HttpPost]toSaveSettings, and moved the AJAX submit handler intoFleets.cshtmlwhere it registers reliably on page load. - Mining Op payout donut — wrong empty-state values: fixed the payout split graph showing hardcoded 75% / 10% / 0% before any ore is mined. The graph now reflects the op's actual configured corp tax rate and fleet percentage from the first render. The Fleet % Apply button now also refreshes the panel immediately via
GetJoinedOp()so the donut updates without a manual reload. - Mining Op stale alert — false positives: fixed "Mining ledger attention required" firing incorrectly after every sync cycle. The root cause was treating
APIExpirationas a staleness indicator — butUpdateOpresets it toDateTime.MinValuebefore each cycle andMergeLedgerExpirationonly sets it back on a successful API call, so the ESI response cache (~5–10 min) was expiring between cycles and triggering false warnings. Members are now only flagged as stale when the op has synced at least once but a member'sAPIExpirationis still the reset default, which means their specific API call failed. Also tightened the "no sync" banner so it only fires after the op has been running for more than 30 minutes without a successful sync.
New Features
- Mining Op ISK/hr: added a live ISK/hr KPI to the op banner (gross op value ÷ op duration), visible once ore data exists. Added per-member ISK/hr below each miner's estimated payout in the contributions panel, calculated from each member's individual join time.
- Mining Op auto-end on inactivity: ops now automatically close after 1 hour with no new mining activity. Added
LastActivityTimetoMiningOp(migration20260314_MiningOpActivity), updatedLedgerProcessorto stamp it whenever ore quantities increase during a sync, and added an inactivity check toTaskProcessor.Schedulerthat ends the op when no activity is detected for ≥ 1 hour on an op that has been running ≥ 1 hour and has had at least one successful sync. - Mining Op auto-name: if a user creates an op without entering a name, the op is automatically named
CorporationName - UserName - MM/dd/yyyy. Public ops (no corp context) useUserName - MM/dd/yyyy. - Mining Op leaderboards: added two leaderboards to the ore breakdown panel — Most Ore Mined (raw ISK value contributed per pilot) and Highest ISK/hr (after-tax payout ÷ time in op). Both match the LP Top Point Earners style: gold/silver/bronze rank colouring, portrait avatars, and relative progress bars. Hidden until after the first sync.
- Mining Op sync ring: replaced the plain "Next sync" text in the Sync Health card with an SVG circular countdown ring. The arc drains as the cycle runs down, turns amber in the final 25%, and glows. Countdown text and "next sync" label sit in the centre of the ring.
Bug Fixes
- Mining Op — miner payout wrong value:
LedgerPayoutCalculatorwas multiplying each member's raw ore value byTaxRate / 100instead of1 - TaxRate / 100. With a 10% tax rate, miners were receiving 10% of the value (the tax amount) instead of 90%. Fixed. - Mining Op — sync refresh gap: after the countdown hit zero there was no automatic data refresh.
Dashboard.jsnow fast-pollsGetJoinedOpevery 3 seconds once the countdown expires and stops as soon as new data arrives. - Mining Op — sync cycle UX gap: changed the sync schedule from 10 to 11 minutes so the backend almost always fires before the ring fully empties, preventing the ring from sitting at zero while users wait.
- Mining Op — duplicate ISK label: the Total Op Value strip was showing "1,350 ISK ISK" because
ToIsk()already appends "ISK" and the template had a hardcoded suffix. Removed the duplicate.
Bug Fixes
- Leaderboard — gifted users excluded: fixed the Top Point Earners leaderboard showing no entries despite corp points existing. The leaderboard was filtering users by
CorporationId, which excluded users who received gifted points from outside the corp. BothGetTopPointEarners()andTopPointEarners()now look up users by their actual UserPoint user IDs so gifted members appear correctly. - LP Store — item icons: fixed store items always showing a generic box icon. Added a
LookupItemTypeIdendpoint that resolves EVE type IDs by exact item name against the local item database, added a live icon preview in the LP store setup form that updates as you type, and saves the resolved type ID so the store now shows real EVE item icons for in-game items. - Settings forms — full-page navigation: fixed Corp Tax Rates, Logistics Settings, System Settings, and Fleet Up API Settings save buttons navigating away to raw JSON instead of saving in-place. All four forms now submit via AJAX and display a success or error toast notification.
- Refining skills — silent failure on scope redirect: fixed the "Set My Skills for Mineral Refining" button silently doing nothing when the user lacked the
esi-skills.read_skills.v1scope. A case mismatch (data.Urlvsdata.url) was preventing the re-auth redirect from ever firing. Added success and error toast feedback so users know when refining skills have been updated or a server error has occurred.
Changes
- Improved the Patch Notes page readability with a dedicated page shell, clearer typography, collapsible date cards, and per-section expand controls for long update lists.
- Cleaned up another warning-reduction batch by tightening OAuth state-token nullability, fixing compact controller warning clusters in
BulletinsController,BugReportsController,AllianceController,FittingController,FleetController, andHistoryController, and clearing smaller framework-noise warnings inLookupController,RecruitmentController, andNavigationViewComponent. - Continued the DbContext refactor by adding a DI-backed
ProcessServicesbundle, registeringTaskProcessorthrough DI, and moving the hosted task processor plus the manualRefreshUsersflow off directAppSettings.CreateContext()andnew TaskProcessor()construction. - Moved
CoreProcess,LedgerProcessor, andPayoutProcessdefault-context creation onto the sharedBaseProcess.CreateContext()path, updatedCoreProcess.GetSkills()to use that central helper, and removed the last active no-contextnew CoreProcess()path fromHomeController.UpdateSkills. - Moved
BugReportsControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from the bug report index, submit, detail, reply, admin, status-update, and attachment flows. - Moved
OrderControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from order submit, current-order, fulfillment, confirm, and cancel flows while keeping the existing per-requestOrderProcessor(_Context)path. - Moved
HistoryControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from personal history, corp profits, op history, bounty history, mining-op totals, and personal mining-history detail flows. - Moved
FleetControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from fleet auth checks, active/current fleet views, pending/confirm/reject flows, settings/details/history search, and fleet-type management. - Moved
MiningControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from mining ledger, invoices, payment matching, top-miner, mining-op join/share/public-buyback, and fleet-settings flows. - Moved
UserControlsControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from mail, blacklist search, character/alt management, scopes, daily graph data, loyalty-point merge, and wallet-journal flows. - Removed the remaining
AccountControllercalls to the staticAppSettings.CreateContext()bridge and routed the add-alt, dev-login, item bootstrap, callback, and logoff cleanup flows through the already-injectedIDbContextFactory<EHRContext>instead. - Updated Lucky Slot Machine status handling so the page now shows the real disabled reason on load instead of always ending on
Ready. Press spin.when daily limits or empty prize inventory have already disabled the button. - Moved
OrderProcessorandUpdateRefiningProcessonto the sharedBaseProcesspattern, removed their remaining directAppSettings.CreateContext()constructors, and registered both process types in DI for the next controller-service migration slices. - Moved
BulletinsControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from bulletin list, load, create, save, and delete flows. - Moved
StoreControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from store group, item, item-part, and setup-list flows. - Moved
StructureControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from structure scope, access-check, info, settings, and save-settings flows. - Moved
SRPControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from SRP history, payout, submit, approval, killmail, and form-management flows. - Moved
LookupControlleroff the remaining staticAppSettings.CreateContext()bridge path and onto injectedIDbContextFactory<EHRContext>for its authenticated character-contact lookup flow. - Moved
FittingControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from fitting-checker, import, purge, and fitting-viewer flows. - Moved
AllianceControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from alliance setup, invite, join, approval, member, banner, and executor-switch flows. - Moved
LogisticsControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from logistics orders, permissions, volume-pricing, and fulfillment flows. - Moved
DiscordControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from Discord auth, server setup, alerts, title mapping, and corp/alliance auth flows. - Started a bounded
SetupControllerconversion by moving the setup dashboard, Patreon benefits, Patreon validation, director-scope option helper, and refining-percentage flows off the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>. - Extended the bounded
SetupControllerconversion through pricing and user-management, moving the set-prices, price refresh, permission, approve/delete user, and setup-save flows off the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>. - Extended the bounded
SetupControllerconversion through pricing-type builders and price overrides, moving calculator-type, formula-row, and override-management flows off the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>. - Finished the remaining
SetupControllerbridge cleanup by moving reset, custom links, permission editor, system settings, scope setup, director-only, and notification-alt flows off the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>. - Moved
RecruitmentControlleroff the staticAppSettings.CreateContext()bridge and onto injectedIDbContextFactory<EHRContext>, removing controller-local ambient context creation from recruitment login, corp application, application review, mail/history, form builder, notes, and related management flows. - Started a bounded
HomeControllerconversion by injectingIDbContextFactory<EHRContext>and moving the corp header, user management, vacation toggle, and scheduled-task management flows off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through the corp-members payload path, moving the DB-backedGetCorpMembersExternal,GetCorpMembersJson, andResolveCorpNamescontext creation off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through corp-member alt/search, mining-op, payout, haul submit/preview, pending-order, op-detail/end, laser-tracking, and ore-price-per-m3 flows, moving that mining/order block off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through the mining-op lifecycle block, moving join, leave, start, refresh-ledger, mining-op-member, and corp-ledger-total flows off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through the order-confirmation, price-adjustment, top-miner, and loyalty-setup block, moving confirm/cancel/validate, blocked-user, price-adjustment, point-setup, and loyalty-item management flows off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through point-history, bulletin/store, slot-machine, notification/gifting, fitting, calendar, and FleetUp setup flows, moving those controller and helper paths off the staticAppSettings.CreateContext()bridge. - Extended the bounded
HomeControllerconversion through panic-message management, navigation info, alt-removal, skills/planets, calendar-check, intel parse, and member-dropdown flows, moving that lower-middle block off the staticAppSettings.CreateContext()bridge. - Finished the remaining
HomeControllercontroller-action bridge cleanup by moving monthly-income, CSV download, item-search, ad-blocker, error-log, store-list, and ESI-audit flows off the staticAppSettings.CreateContext()bridge. - Centralized the remaining static
DropDownsandShortCutscontext access behind localCreateContext()helpers, removing the last scattered directAppSettings.CreateContext()call sites fromHomeController.cswhile leaving those static helper classes queued for a later service extraction. - Moved the embedded
DropDowns,ShortCuts, andHelperClassutility types out ofHomeController.csand intoHomeControllerUtilities.cs, reducing controller file sprawl without changing the existing helper call sites yet. - Added an injected
IShortcutService/ShortcutServicepath, registered it in DI, and moved the current controller-sideShortCutsusages inHomeController,MiningController,SetupController,FleetController,FittingController, andSRPControlleroff the static helper while leaving the existing Razor, helper, and process callers for a later slice. - Added
IOrderWorkflowService/OrderWorkflowService, moved theOrderControllersubmit/current/fulfillment/confirm/cancel business logic into that service, and added an explicit-contextOrderProcessor.ProcessesNewOrder(...)path so the order flow no longer needs controller-ownedOrderProcessorconstruction. - Added
ILogisticsOrderWorkflowService/LogisticsOrderWorkflowService, madeParseItemsProcessinjectable throughProcessServices, and moved the active logistics place/update order workflow out ofLogisticsControllerso that controller no longer ownsPricingProcessorParseItemsProcessconstruction for those paths. - Added
ICoreWorkflowService/CoreWorkflowService, registered it in DI, and moved the activeHomeControllernotification, loyalty-point tally, and skill-refresh paths off directnew CoreProcess(_Context)construction while leaving the payout-heavy mining-operation flows for a later slice. - Moved the shared principal/auth snapshot helpers in
Extensions/UserExtensions.csonto centralizedCreateContext()bridge points, changedGetUserId()to prefer the existing auth claims, pushedAllianceSwitchandDirectorinto the cached auth snapshot, and removed the per-request layout lookup for alliance mode by servingGetAllianceSwitch()from the snapshot cache with a DB fallback only when claims are incomplete. - Removed the remaining constructor-time
AppSettings.CreateContext()calls fromModels/HomeViewModels.cs, changed the mining-op start and ore-entry models into pure data containers, and moved mining-op start-form plus ore select-list population intoHomeControllerso the_MiningOpspartial no longer constructs a DB-backed view model from Razor. - Removed the dead constructor-time
AppSettings.CreateContext()load fromModels/SetupViewModels.cssoSetupViewModelis now a pure container, and changedHelpers/JsonErrorResult.csto use claim-backed corp and user IDs for error logging with a centralizedCreateContext()fallback instead of always opening a context just to resolve the current user first. - Added context-aware
AuditLog.AddLog(...),UserPoint.IncreasePoints(...), andUserPoint.SpendPoints(...)overloads inModels/Models.cs, switched the activeCoreProcess,TaskProcessor,HomeController, andLogisticsControllerpoint and audit flows onto their existingEHRContext, and removed those live paths from the hidden second-context pattern that was still writing point history and audit rows throughAppSettings.CreateContext(). - Removed the orphaned
SessionData(string username, IPrincipal user)constructor-sideAppSettings.CreateContext()load fromModels/Models.csand leftSessionDataas a plain data container, since the type no longer has any live call sites in the repo. - Centralized the active
APIHelperauth and logging context creation behind a localCreateContext()helper, extracted shared user and scope token-refresh persistence and bad-request update routines, and moved the active ESI access logging, token refresh, user-scope refresh, price lookup cache fill, ID search, director check, and name-resolution paths off scattered directAppSettings.CreateContext()calls. - Removed the remaining live
APIHelpersearch overload that reloadedUserby ID from the database, switched the logistics pricing and parse-items flows to pass the already-knownUserobject instead, and dropped that hidden lookup path from the active search call chain. - Marked the
OrderProcessor(ProcessServices)constructor as the DI activation path so service-provider validation no longer sees an ambiguous choice between the DI-backed process constructor and the manualOrderProcessor(EHRContext)controller path. - Made the
OrderProcessorservice registration explicit inProgram.csso DI now always constructs it fromProcessServicesinstead of reflecting over the available constructors during service validation. - Made the
UpdateRefiningProcessservice registration explicit inProgram.csso DI now always constructs it fromProcessServicesinstead of reflecting over the available constructors during service validation. - Added
IPayoutWorkflowService/PayoutWorkflowService, registered it in DI, and moved the activeHomeControllerhaul-submit, mining-op lifecycle, op-confirmation, and bounty-refresh paths off directnew PayoutProcess(_Context)construction while also removing the dead confirm-flowPricingProcessallocations in that block. - Added
IPricingWorkflowService/PricingWorkflowService, registered it in DI, removed the dead haul-sidePricingProcessconstruction inHomeController, and moved the activeHomeControllerore-price-per-m3 and price-adjustment cache fill plusSetupController.GetPrices()refresh flow off direct controller-ownednew PricingProcess(...)construction. - Extended
AppSettingswith DI scope creation, expandedIShortcutService/ShortcutServiceto cover the remaining static shortcut operations, and convertedHomeControllerUtilities.ShortCutsinto a thin compatibility wrapper that resolves DI services per call so the existing Razor, process, and legacy helper call sites no longer create their ownDbContext, lookup client, or character client instances directly. - Extended
ICoreWorkflowService/CoreWorkflowServicewith a fleet-points path and movedFleetController.Confirmation(...)off directnew CoreProcess(_Context)construction, leaving the fleet confirmation action on its existing request context while removing the last controller-owned core-process creation in that file. - Extended
ICoreWorkflowServiceandIPricingWorkflowServicewith context-aware killmail, bounty-point, and sale-price operations, then movedKillMailProcessand the activePayoutProcessbounty and ledger-payout paths off directnew CoreProcess(...)andnew PricingProcess(...)ownership by resolving those workflow services through the shared compatibility scope instead. - Extended
IPayoutWorkflowServicewith a context-aware bounty loader, removed the deadOrderProcessorPayoutProcessallocation, and moved the activeTaskProcessorbounty sweep and mining-op scheduler payout refresh paths off directnew PayoutProcess(_Context)construction and onto the shared payout workflow service. - Removed the remaining dead internal
PricingProcessself-construction inGetPrice(...)andUpdatePrice(...)plus the unusedPayoutProcessallocation inAddOpMinerals(...), trimming the leftover process-to-process noise fromPricingProcess.cswithout changing runtime behavior. - Moved the active
LedgerProcessormining-ledger price resolution off the staticShortCuts.GetPrice(...)bridge and ontoIPricingWorkflowServiceresolved through the shared compatibility scope, so those live process paths now use the request/processEHRContextdirectly instead of bouncing through the legacy shortcut wrapper. - Moved
ShortcutServiceprice lookups ontoIPricingWorkflowServiceso the service no longer constructsPricingProcessdirectly, and fixed theGetPrice(..., pricingTypeId)overload so it now actually passes the requested pricing type through instead of silently ignoring it. - Changed the DI-created
APIHelperpath to createEHRContextinstances from injectedIDbContextFactory<EHRContext>instead of the staticAppSettingsbridge, moving the live token-refresh, ESI-access logging, cached name lookup, price fill, and director-role helper paths onto the shared factory-backed context pattern. This also fixes the staleIsInDirector(string)cache branch so non-directors are no longer cached as directors. - Changed
SignalRServiceto use injectedIDbContextFactory<EHRContext>for its corp-user and op-member lookup helpers, removing the remaining real-time membership lookup paths from directAppSettings.CreateContext()usage. - Changed
Models.KillmailNamesto resolveILookupApiClientthrough the shared compatibility scope instead of constructing a rawAPIHelper, removing the last livenew APIHelper()path from the killmail Discord embed helper. - Changed
ParseHelperto use injectedIPricingWorkflowServicefor buyback preview pricing and changedNotificationsHelperto resolveIShortcutServicethrough the shared compatibility scope for notification name, item, and price formatting, removing another live batch of directShortCutscompatibility-wrapper calls from helper code. - Added
IShortcutServiceto the process-service bundle and moved the liveTaskProcessorstructure-fuel and Discord role-removal message formatting paths off the staticShortCutscompatibility wrapper and onto the injected shortcut service, while keeping default legacy processes on a small fallback adapter. - Fixed the
NotificationsHelperscoped-service lifetime regression by keepingIShortcutServiceinside the active compatibility scope for the full formatting pass, and added context-awareLog.Error(...)/Log.Audit(...)overloads soJsonErrorResultnow writes its error log with a singleEHRContextinstead of opening separate read and write contexts on the same error path. - Changed
HomeControllerUtilities.DropDownsto create its context through the registeredIDbContextFactory<EHRContext>resolved from the compatibility scope instead of callingAppSettings.CreateContext()directly, removing the last direct static context bridge from that utility file. - Collapsed
HomeControllerUtilities.ShortCutsonto a single scopedIShortcutServiceexecution helper and changed theBaseProcesslegacy shortcut fallback to resolveIShortcutServicedirectly from DI instead of bouncing back throughControllers.ShortCuts, reducing duplicate compatibility-wrapper code while keeping the existing Razor and default-process call sites working. - Removed another batch of direct
AppSettings.CreateContext()usage by moving theBaseProcessfallback path,KillMailProcess,ErrorLogging,JsonErrorResult,AuditLog,UserPoint, and the remainingAPIHelperfallback path ontoIDbContextFactory<EHRContext>resolved from the shared compatibility scope instead of the old static context bridge. - Tightened the active
HomeControllerguard paths by makingIndex()tolerate a missing identity, returningEmptyResultinstead ofnullfromCorpHeader(), and validating usernames or test users before calling the skill-refresh, bounty-load, and mining-ledger helper paths. - Tightened another
HomeControllernullability slice by making the internalGetUserInfo(...)helper nullable-aware, guarding loyalty-order notification sends behind a real username, returning a JSON success response instead ofnullfor no-op haul corrections, returningEmptyResultfrom the getting-started partial when no work is needed, and replacing the brokenDateTime == nullCSV export checks with explicit empty-date validation. - Removed another
HomeControllerwarning cluster by dropping the impossible calendar-events null return, validating the current username and user before building the skills and planets views, and makingGetErrors()tolerate missing identities while filtering null log entries before the final grouped JSON response. - Tightened the
HomeControlleralt-removal and monthly-income paths by guarding current-user name lookups before corp resolution, making the live monthly-income builder bail out cleanly when no current user is available, making the error-list accumulator explicitly nullable, and marking the monthly-income summary view model's optional highlight cards as nullable when a period has no matching categories. - Made the shared
BaseController/OAuthFlowHelperauthorization URL helpers accept nullable return and state values, tightenedGetSafeLocalReturnUrl()around missing referers, and added current-user guards to the activeUserControlsControllermail flows before loading users or checking ESI mail scopes. - Tightened the remaining
UserControlsControllercurrent-user and character-management paths by guarding the blacklist search, characters, scope purge, daily graphs, point-merge, wallet journal, main-character swap, and alt-removal actions, and by giving the chartDataSetDTO safe default string values instead of leaving those fields uninitialized. - Removed the remaining nullable-signature warnings from
JsonErrorResultandErrorLogging, hardened the reset-password partial against a missing identity, tightened the activeAccountControllerauth and callback paths with nullable-safe parameters and guards, and cleaned upUserExtensionswith safe snapshot defaults, nullable-aware helper signatures, andRandomNumberGenerator-based list shuffling. - Tightened another
HomeControllerslice by guarding scheduled-task deletion and creation, fitting asset search, SMS gateway updates, panic-message editor/send/delete flows, getting-started, alt removal, and nav info against missing records or identities, while also finishing the remaining easy nullable and auth fixes inAccountController.
Fixes
- Removed the stale commented-out code blocks that were still scattered through the active controllers, helpers, processes, and app-owned JavaScript files, leaving only real explanatory comments and TODO notes in the live source.
- Changed the Eve SSO callback token-persistence flow so Eve-HR now only stores the fresh token pair after the callback scope set satisfies the corporation's required scopes, and it now sends the user back through the correct corp-scope re-auth flow when neither the callback token nor the stored token matches the corp requirement.
- Fixed the authenticated recruitment apply flow so it now re-verifies the live token scopes before redirecting for SSO and only creates the application record after the scope check passes, preventing false scope redirects from stale stored scope strings.
- Fixed the Setup refining-skills action so it now uses the newer responsive setup button styling and a proper action row, preventing
Set My Skills For Mineral Refiningfrom clipping or collapsing inside the panel. - Fixed authenticated recruitment applications so the app now compares normalized requested and granted ESI scope sets instead of naive comma-split substring checks, preventing unnecessary re-auth prompts when your scopes have not changed.
- Fixed the background mining-op ledger refresh so scheduled updates now load active op members from the database instead of relying on an unloaded
OpMembersnavigation, preventing live ops from appearing to have no active members during the scheduler pass. - Fixed the Eve SSO callback so fresh logins now immediately adopt the newly returned token pair and clear stale revoked-token state before the follow-on scope and ESI checks run, preventing valid sign-ins from failing with a restore-ESI error.
- Fixed the buyback parse preview database path by ignoring the dead EF
Haul.Itemsnavigation that was generating the invalid MySQLi.HaulIdquery, and tightenedParseHelperso newly resolved items are only inserted once before preview history is built. - Fixed the shared buyback preview partial so it now renders the correct submit-button class for the home dashboard versus the public buyback page, restoring the final
Submit Orderclick on the authenticated buyback screen. - Fixed the authenticated home buyback submit handler so successful submissions now show a real completion state and failed submits surface the server error instead of appearing to do nothing.
- Changed the shared home and public buyback submit buttons so once clicked they immediately switch to a muted
Submittedstate and disable further clicks while the request is in flight, reducing duplicate submissions and making the control read as completed instead of active. - Replaced the scope re-auth page's plain text link treatment with dedicated standalone CTA button styling so
Continue to Re-authandBack to Loginread like real buttons on the authorization screen. - Replaced the legacy tiny
Submitinput on the buyback setup tax-rates card with a dedicated styledSave Settingsaction button so that panel now matches the newer setup UI. - Added responsive breakpoints to the Pricing Control page so the calculator list, tab bar, override form, and formula builder step cards now stack and shrink cleanly on narrower widths instead of holding the oversized desktop layout.
- Changed mining-op ledger refresh so per-character ledger fetch failures, null payloads, and empty-result passes are treated as non-fatal skips, allowing the op update to continue across every linked character instead of aborting on the first bad ledger response.
- Fixed grouped mining-op ledger expiration handling so a main-alt op member now keeps the freshest successful ledger authorization across linked characters instead of being falsely marked stale by another linked character's older or failed token.
- Removed the obsolete
Price AdjustmentsandPrice Overridesentries from the buyback sidebar so only the remaining supported setup tools stay in navigation. - Removed the dead controller-owned recruitment snapshot and cache helper block from
RecruitmentControllerafter moving the live path ontoIRecruitmentSnapshotService, so the hosted snapshot service is now the only active implementation. - Removed the mutable process-layer error flags from
BaseProcess, replaced the oldError()/HasError()/ErrorMessage()flow with exception-backedProcessResultcapture, and converted the active core, ledger, payout, pricing, parse-items, and task-processor callers onto shared typed client properties instead of per-call factory helpers. - Reduced the remaining direct
APIHelperusage inHomeControllerby moving buyback parsing ontoILookupApiClient, swapping more name and universe lookups onto narrower clients, and converting the staticShortCutshelper to use lookup and character clients instead of raw helper calls. - Removed the raw
APIHelperdependency from the recruitment mailer helper and kept the mailer flow on typed lookup and mail clients. - Removed the mutable
APIHelpercontroller error-state compatibility layer and moved the typed operation wrappers onto direct exception-to-result handling so helper calls no longer share request-local error flags. - Replaced the remaining
HomeControllerWebClientcalls withHttpClient, switched the flagged partial views to async-safe<partial>rendering, and removed the startup raw-SQL interpolation warning by using fixed SQL strings. - Finished the
BaseControllerconstructor-injection transition through the sharedControllerServicesbundle, moved anotherHomeController,RecruitmentController, andTaskProcessorslice off rawAPIHelpercalls, and removed the blocking wait fromKillMailProcessshutdown by switching it to cancellation plusStopAsync(). - Finished the
APIHelperESI refactor by migrating the remaining contacts, contracts, mail, wallet, structures, and related endpoints offWebClient, and removed the old token-refreshTask.Run/.Wait()sync-over-async flow. - Reduced the next-phase API refactor risk by moving injected
APIHelperusage off singleton lifetime, adding a typed internal HTTP result path for clearer failures, and making the calendar background worker honor cancellation instead of blocking on rate-limit delays. - Started replacing the legacy
HasError()controller contract by adding typed operation-result wrappers for mail, wallet, notifications, and market orders, and migrated the main User Controls and Recruitment screens for those flows onto the new pattern. - Extended the typed-result migration to structures, fleet, mining-ledger, skills, and skill-queue reads, and fixed the active-fleets partial so assembled fleet view models are actually returned for rendering.
- Continued the typed-result migration through alliance banner/member checks, SRP killmail loading, setup skill lookups, mining-op contract validation, fitting asset scans, and task/ledger background processing so more ESI paths no longer depend on mutable
APIHelpererror state. - Moved the auth and Discord role-assignment paths onto the newer typed wrappers for alliance, titles, corp affiliations, alt token refresh checks, and explicit-token character role reads.
- Pushed the typed-result migration deeper through recruitment snapshots, contacts/history/contracts/killmail views, wallet name-resolution paths, SRP killmail aggregation, and the director-role helper so those screens no longer depend on ad hoc
HasError()checks. - Added a real async token-refresh persistence path for async callers, moved the account callback and task processor onto it, and converted another
HomeControllerslice for names, character/corporation lookups, status, planets, and skills onto typed helper wrappers. - Added explicit result wrappers to
LedgerProcessorfor start/join/update flows, moved the mining-op controller and background call sites onto them, and converted the nav summary plus remaining contract-role checks inHomeControlleronto typed helper wrappers. - Extended the explicit-result pattern into
PayoutProcessandCoreProcess, moved the active mining-op and notification call sites onto those results, and replaced the old fire-and-forgetUpdateSkillserror check with a direct result-based refresh. - Converted
ParseHelperto an explicit parse-result contract for the active buyback order and haul preview flows, removing the last live parserHasError()checks from thatHomeControllerslice. - Added typed wrappers for corporation journal transactions and fittings, and moved the remaining monthly-income, mining-payment, fitting-sync, and contract-item validation paths in
HomeControllerandMiningControlleroff raw helper calls. - Added typed wrappers for blacklist search and blacklist name resolution, and moved the remaining alliance-management and user-controls search, member lookup, journal, and name-resolution paths off raw helper calls.
- Added typed wrappers for universe, item, route, and alliance-history lookups, and moved the logistics controller, lookup popups, account item import, recruitment mailer/search paths, and the remaining home/task/discord/core-process raw helper callers onto explicit operation results.
- Added typed wrappers for ID resolution, corp-title reads, and bulk-price fetches, then moved the remaining SRP history, recruitment pricing/mailer, Discord title sync, task-processor fleet and structure polling, and core-process skill-refresh reads off raw helper calls.
- Added typed wrappers for market-price, buy/sell, and direct mail-send helper calls, then moved the remaining pricing fallback paths, subscription mail sender, and parse-time item resolution off raw helper calls while fixing the null pricing-mapping branch in
PricingProcess.GetPrice. - Extracted reusable OAuth URL and state helpers, moved controller scope-authorization redirects off ad hoc
APIHelperconstruction, and wired the fleet, structure, mining, contracts, and custom-scope flows onto explicit state-token generation with their callback flow names preserved. - Centralized named-scope checks behind a shared helper, fixed the brittle fleet-scope substring check to evaluate required scopes individually, removed dead
APIHelperscope wrappers, and trimmed more obsolete auth compatibility code fromAPIHelper. - Centralized controller-side
APIHelperaccess behind a sharedBaseControllerhelper and replaced the remaining active controllernew APIHelper(this)call sites so ESI-backed controller flows no longer construct ad hoc helpers throughout each file. - Centralized process-side
APIHelperconstruction behind a sharedBaseProcessfactory helper and replaced the activenew APIHelper()calls across task, pricing, payout, core, ledger, and parsing processes so the remaining background ESI flows stop scattering direct helper construction through each method. - Replaced the last active ad hoc
APIHelperconstructions inShortCuts, recruitment snapshot refresh workers, and the internal director-role cache path with small shared helper and factory methods so only the intentional centralized creation points remain. - Added a dedicated
ILookupApiClient/LookupApiClientservice for read-only universe and reference-data calls, registered it in DI, exposed it throughBaseController, and movedLookupControllerplus the logistics route, item, system, and search flows onto that narrower client instead of the fullAPIHelperfacade. - Added a dedicated
IMailWalletApiClient/MailWalletApiClientservice for mail, wallet, notifications, and market-order ESI calls, registered it in DI, exposed it throughBaseController, and moved the mainUserControls,Recruitment, andHomeControllermail/wallet flows onto that narrower client. - Added a matching process-side mail/wallet client factory in
BaseProcessand moved the activeTaskProcessornotification and advert-wallet polling paths plusCoreProcesssubscription mail sending ontoIMailWalletApiClientinstead of the broaderAPIHelperfacade. - Added a matching process-side lookup client factory and moved the remaining active character, corporation, and alliance reads in
AllianceController,AccountController,DiscordController,HomeController,RecruitmentController, andCoreProcessontoILookupApiClientinstead of directAPIHelperlookup calls. - Added a dedicated
IAuthApiClient/AuthApiClientservice for SSO verify and token-refresh operations, exposed it through the shared controller and process helpers, moved the active account callback and token-refresh worker paths onto it, and removed the deadISingleSignOnClientregistration and unusedAPIHelperinterface shim. - Removed the misleading synchronous
GetRefreshTokenAsyncpath insideAPIHelper, renamed the remaining sync and async token refresh helpers to clearer internal names, and rewired the shared access-check path onto a newEnsureAccessTokenhelper without changing caller behavior. - Added a dedicated
ICharacterApiClient/CharacterApiClientservice plus controller and process factory accessors, and moved the active roles, skills, skill queue, attributes, contacts, corp-history, assets, structure-name, location, ship, and online-state reads across Home, Recruitment, Fleet, Fitting, Account, Alliance, Setup, Structure, TaskProcessor, and CoreProcess off directAPIHelpercharacter-profile calls. - Added a dedicated
IOperationsApiClient/OperationsApiClientservice plus controller and process accessors, and moved the active fleets, contracts, mining-ledger, calendar, and planet reads across Fleet, Home, Mining, Recruitment, TaskProcessor, and LedgerProcessor off directAPIHelperoperations calls. - Added a dedicated
ICorporationApiClient/CorporationApiClientservice plus controller and process accessors, and moved the active corporation members, corp-contract, corp-title, structure-info, character-title, and corp wallet-journal reads across Alliance, Home, Discord, Structure, UserControls, Mining, TaskProcessor, and PayoutProcess off directAPIHelpercorporation calls. - Added a singleton
IRecruitmentSnapshotServicewith hosted background refresh processing for skills and assets, moved the active recruitment mini views and snapshot-backed endpoints onto async service calls, and centralized the live cache and refresh path outsideRecruitmentController.
Changes
- Removed the ad-cookie approval banner and manual advertising-consent flow, and simplified the privacy/footer links so the site no longer exposes cookie-settings controls.
Fixes
- Fixed mining-op ledger aggregation so refreshes, joins, leave-op handling, and payout snapshots resolve through the root main account and linked sibling alts.
- Fixed mining warning copy and stale-ledger detection so empty mining results no longer trigger false member warnings.
- Fixed ESI token refresh persistence and moved mining-ledger/corporation-member ESI calls onto a safer HTTP path with stricter response handling.
Changes
- Added the shared sidebar collapse toggle and then refined it into a lighter chevron-only control.
Fixes
- Fixed the Mining Ops timer, end-operation refresh flow, live page auto-refresh, and multiple Mining Ops UI alignment issues across the redesigned command deck.
- Expanded mining ledger warning details, tightened Lucky Slot Machine winner privacy, and refreshed the shared confirmation dialog styling.
- Fixed the Setup/Prices step-number rail alignment regression.
Changes
- Updated the patch notes workflow so new changes create the correct current-day section and the in-app Patch Notes page stays synchronized with
PATCHNOTES.md. - Added a real on-page
3/8/2026patch notes card and refreshed the rendered summaries so today’s work is visible in the app. - Redesigned the standalone scope re-authentication screen to match the newer EVE-HR visual language.
- Expanded Lucky Slot Machine stats with total spins, total wins, total win rate, last winner, and progressive winnerless-day odds.
- Updated Bug Admin so closed reports move into an expandable history section instead of remaining in the active queue.
Fixes
- Fixed Corp Members ESI handling around claimed-director scope refresh, alliance-exec queries, and error reporting for failed external member checks.
- Fixed bug report admin navigation, filter alignment, and detail-page chevron positioning.
- Fixed Mining Ledger, Mining Ops CSV, and Fittings UI alignment problems caused by legacy button and checkbox styling.
- Fixed the Bulletins Calendar Events layout by replacing the problematic table treatment with a cleaner board layout.
Changes
- Added the full in-app bug reporting system with required screenshots, personal tracking, private image storage, and an Ascorbic-only admin queue.
- Expanded release tooling with a dedicated publish-zip script, VS Code task support, and a tracked
releases\output folder. - Refreshed major UI areas including bug reports, Director Only, Alliance Join, Fleet tools, SRP, ESI Audit, Recruitment setup pages, and the Price Builder to better match the newer EVE-HR theme.
- Added a shared SVG favicon so browser tabs and bookmarks use EVE-HR branding.
Fixes
- Fixed bug report submission, routing, explicit navigation paths, and admin screenshot previews.
- Fixed Add Alt and recruitment application OAuth issues including stale scope validation, oversized callback state, and post-auth redirect/login expiry problems.
- Improved performance on User Management, Recruitment Current Member, and page authorization checks by reducing duplicate queries and parallelizing initial loads.
- Fixed multiple legacy UI regressions including broken toggles, clipped buttons, white/grey editors, and inconsistent page layouts across Recruitment, Alliance, Fleet, and Setup screens.
Fixes
- Recruitment Current Member: fixed the Corp History sidebar list so logos, names, and dates render in a readable compact layout.
- Recruitment Current Member: corp names in Corp History now truncate cleanly with ellipsis instead of overflowing the sidebar.
- Recruitment Current Member: removed the unnecessary secondary Corp History label text to reduce visual clutter.
Changes
- Recruitment ads: updated Patreon Supporters to use the same card presentation as Featured Corporations, with a Patron-specific accent treatment.
- Recruitment Corp History: refreshed the full and mini card styling to better match the current site theme.
Fixes
- Navigation accordion spacing and background-color regressions were corrected across the sidebar and loyalty pages.
- Personal Mining History fixes addressed row overlap, readability, and Export CSV button rendering.
- Logistics Orders row expansion and clipboard-copy behavior were corrected for the redesigned layouts.
- Loyalty/Gifting now validates and loads the same accessible member scope before points are applied.
Changes
- Rebuilt the left navigation as an accordion and standardized the newer blue-theme navigation styling.
- Redesigned Loyalty Pending Orders, Personal Mining History, User Management, User Store, Logistics Orders, Point History, and Gifting.
- Applied a broader legacy-page theme override and added runtime toggles for background hosted services.
Changes
- Improved pricing performance by fetching missing mineral prices in parallel instead of serially during cache misses.
Changes
- Large recruitment performance pass: added cached mini/snapshot endpoints, stale-while-revalidate behavior, shared skill/name caches, lighter JSON mini payloads, and deferred/lazy rendering for expensive Current Member sections.
- Optimized Corp Members with cached corp-name resolution and reduced per-corp lookup overhead.
Fixes
- Fixed several recruitment null/stale-token error paths and corrected
Setup/DirectorOnlyfallback handling when the Director claim is stale or missing.
Fixes
- Removed loading elapsed time/counter text from Ore Values loading states.
- Removed link-level block overlay on Buy Back navigation links so the dark box no longer appears over link text.
- Patron access checks now read from auth snapshot/corp state instead of stale claim-only paths, so enabled patron status applies correctly.
- Killmail broadcast dedupe now uses a consistent corp/alliance key path, preventing duplicate or skipped attacker broadcasts.
Changes
- Ore Values: replaced the small legacy spinner with a new centered animated orbit loader and consistent loading message.
- Ore Values: updated AJAX body loading to immediately clear old content and show loading UI while prices are calculated.
- Price Adjustments: migrated to the same AJAX body-load flow used by Ore Values, including centered loader and in-panel render.
- Navigation: added shared Home panel loading helper for consistent loading/error UI across GetPricePerM3 and PriceAdjustments.
- Setup: added a Patreon validation action/button that checks active patron status and enables corp patron benefits (
Corp.Patron) when valid. - Setup UX: moved Patreon Benefits into a dedicated
Setup/PatreonBenefitspage and added a separate Setup navigation link. - Ads: refactored layout ad integration to one global AdSense script include with responsive ad slots and reserved ad space to reduce layout shift.
- Ads: removed custom ad-block pop-up flow so AdSense-managed ad-block recovery/offerwall can be used instead.
- Ads platform: added
ads.txtat/ads.txtfor AdSense crawler discovery. - Killmail worker: added periodic cache refresh during runtime, payload null guards, and host-linked cancellation/shutdown handling.
Fixes
- Recruitment/Assets: fixed
location_flagparsing so new ESI values likeInfrastructureHangarno longer throw a 500 during asset load. - Recruitment/Assets: updated structure filtering to treat both
HangarandInfrastructureHangaras valid structure hangar locations. - Recruitment/CurrentMember: fixed the GetAssetInfo sort toggle click handler so sorting now reliably toggles between total ISK and alphabetical order.
Changes
- Recruitment/Assets: added a sort toggle in GetAssetInfo so asset locations can switch between alphabetical and highest total ISK ordering.
- Documentation: added USER_GUIDE.md with user-facing workflows for navigation, recruitment, mining, logistics, loyalty points, and setup roles.
- Documentation: expanded USER_GUIDE.md with a detailed, step-by-step setup/configuration tutorial for admins, including director scopes, pricing setup, permissions, scope policy, Discord, structure alerts, and recruitment setup.
Fixes
- Recruitment: fixed GetSkillInfo so returned JSON includes populated SkillQueue and SkillsList content.
- Recruitment: fixed GetAssetInfo rendering path so assets returned from API now display correctly on-screen.
- Recruitment UI: updated AJAX handlers to support object JSON first, with safe string-JSON fallback and clearer error display.
- Controllers: fixed JSON error paths that called JsonHttpStatusResult.Error(...) without returning the result.
- Setup/UserControls: removed double-serialized JSON responses and aligned payload contracts with current JS consumers.
- Logistics: improved pricing validation so invalid inputs return explicit JSON errors instead of silent failures.
Changes
- Added Patch Notes link to the main left navigation menu.
- Refreshed Recruitment CurrentMember UI with cleaner tabs, improved character summary layout, and responsive styling.
- Adjusted CurrentMember colors to match the existing site theme (dark gray/black styling).
- Updated Skills/Fittings tabs to rectangular tab styling and increased font sizes for easier reading.
- Moved patch notes to Patreon. You can view them here.
Fixes
- Structure alerts should no longer alert every hour, but only once a day.
- Hourly user updates should now actually update their corp information.
New Features
- Loyalty Point Monthly Reset.
Fixes
- Fixed issue with Preview not showing results if item was not found.
New Features
- Loyalty Point History.
Fixes
- Removed New Member Page from setup, was already under recruitment.
- Fixed some typos.
New Features
- !who command now allows you to check who alts belong to in your corp.
Fixes
- Fixed issue with leader of mining op leaving breaking the op.
- Fixed issue with mining ledger failing to update when the main user has an error.
- Increased max value of loyalty points to 9,007,199,254,740,992.
New Features
- Discord Notification for New Mining Ops.
Fixes
- Fixed issue with check box on system settings.
New Features
- Members Page got a facelift.
- Members Page got a Search Button.
Fixes
- Type fixed on logistics page.
New Features
- Added Personal Mining Op History.
Fixes
- Taxes can now have decimals.
New Features
- Added Preview Sale Order function.
- Added Cancel button to Loyalty Points order confirmation page.
Fixes
- Added enum to Journal for gate jumping.
New Features
- Added Mining Ops Histories.
Fixes
- Fixed type on Setup/Setup for efficiency.
- Fixed for parsing ascii characters during submitting sale orders.
- Updated confirmation text for removing discord link from eve-hr.
- Fixed Payout Calculations, you should now see outstanding payments due.
- Fixed loyalty points for mining/sell orders.
New Features
- Mining Invoicing!
- New Bounty System!
Fixes
- Issue with Access Tokens becoming out of sync has been resolved.
- Renamed Rems to Rens...
New Features
- Mining Ledger Viewer now has Include Alts option.
Fixes
- Inverted navigation bar controls. It will now stay expanded until you click on EveHR to minimize it.