feat: move test buttons to right panel, add user auth dialog for reports
Move Start/Stop/Report buttons from the middle panel to the top of TestPanelView (automated tests section), matching the old application layout. Remove inline Operator/Client text fields — operator identity now comes from a UserCheckDialog (username/password) shown before the existing ReportDialog. Add credential storage to ConfigurationService. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,5 +62,16 @@ namespace HC_APTBS.Services
|
||||
|
||||
/// <summary>Saves updated sensor calibration data to <c>sensors.xml</c>.</summary>
|
||||
void SaveSensors();
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Validates a username/password pair against stored credentials.</summary>
|
||||
bool ValidateUser(string username, string password);
|
||||
|
||||
/// <summary>Returns all stored user credentials as a dictionary.</summary>
|
||||
IReadOnlyDictionary<string, string> GetUsers();
|
||||
|
||||
/// <summary>Replaces all stored user credentials and persists them.</summary>
|
||||
void UpdateUsers(Dictionary<string, string> users);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ namespace HC_APTBS.Services.Impl
|
||||
new XElement("ReportLogoPath", Settings.ReportLogoPath),
|
||||
new XElement("KLinePort", Settings.KLinePort),
|
||||
new XElement("Language", Settings.Language),
|
||||
new XElement("Relations", RpmVoltageRelation.Serialise(Settings.Relations))
|
||||
new XElement("Relations", RpmVoltageRelation.Serialise(Settings.Relations)),
|
||||
new XElement("Users", Settings.Users)
|
||||
);
|
||||
new XDocument(root).Save(ConfigXml);
|
||||
SaveSensors();
|
||||
@@ -355,6 +356,7 @@ namespace HC_APTBS.Services.Impl
|
||||
TryString(r, "KLinePort", v => _settings.KLinePort = v);
|
||||
TryString(r, "Language", v => _settings.Language = v);
|
||||
TryString(r, "Relations", v => _settings.Relations = RpmVoltageRelation.Deserialise(v));
|
||||
TryString(r, "Users", v => _settings.Users = v);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -640,6 +642,46 @@ namespace HC_APTBS.Services.Impl
|
||||
try { var v = root.Element(name)?.Value; if (v != null) assign(v); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ValidateUser(string username, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
return false;
|
||||
|
||||
string check = username + ":" + password;
|
||||
foreach (string entry in Settings.Users.Split(','))
|
||||
{
|
||||
if (entry == check) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyDictionary<string, string> GetUsers()
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
foreach (string kv in Settings.Users.Split(','))
|
||||
{
|
||||
string[] parts = kv.Split(':');
|
||||
if (parts.Length == 2 && parts[0].Length > 0)
|
||||
dict[parts[0]] = parts[1];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UpdateUsers(Dictionary<string, string> users)
|
||||
{
|
||||
var entries = new List<string>(users.Count);
|
||||
foreach (var kv in users)
|
||||
entries.Add(kv.Key + ":" + kv.Value);
|
||||
|
||||
Settings.Users = string.Join(",", entries);
|
||||
SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// ── XPath extension shim ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user