✨ feat(admin): add admin device, post, and report features
- add admin endpoints to create devices and update device settings - expose device members and post detail for admin management - add post filters by device, author, type, and date range - add user growth, device uptime, and usage reports with CSV export - improve admin layout with sticky header, user info, and logout - add device and author search filters across admin pages
This commit is contained in:
@@ -113,10 +113,86 @@ public sealed class AdminDeviceDetailDto
|
||||
public string? DevicePhoneNumber { get; set; }
|
||||
public int? UploadIntervalMin { get; set; }
|
||||
public int? RelayCount { get; set; }
|
||||
public string? NeshanLocation { get; set; }
|
||||
public decimal? Latitude { get; set; }
|
||||
public decimal? Longitude { get; set; }
|
||||
public int? MinimumSmsIntervalMinutes { get; set; }
|
||||
public int? MinimumCallIntervalMinutes { get; set; }
|
||||
public decimal? AreaSquareMeters { get; set; }
|
||||
public Domain.SimCardType? SimCardType { get; set; }
|
||||
public string? SmsRecievers { get; set; }
|
||||
|
||||
public IReadOnlyList<TelemetryDto> LastTelemetry { get; set; } = [];
|
||||
}
|
||||
|
||||
// ==================== Devices: create / settings / members ====================
|
||||
|
||||
public sealed class AdminDeviceCreateRequest
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
public string Location { get; set; } = string.Empty;
|
||||
public string? NeshanLocation { get; set; }
|
||||
}
|
||||
|
||||
public enum AdminDeviceCreateOutcome
|
||||
{
|
||||
Ok,
|
||||
UserNotFound,
|
||||
ConflictDuplicateName,
|
||||
Validation
|
||||
}
|
||||
|
||||
public sealed class AdminDeviceCreateResult
|
||||
{
|
||||
public AdminDeviceCreateOutcome Outcome { get; set; }
|
||||
public int DeviceId { get; set; }
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
/// <summary>پیام فارسی برای خطاهای Validation</summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class AdminDeviceSettingsUpdateRequest
|
||||
{
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
public string Province { get; set; } = string.Empty;
|
||||
public string City { get; set; } = string.Empty;
|
||||
public decimal? Latitude { get; set; }
|
||||
public decimal? Longitude { get; set; }
|
||||
public string ProductType { get; set; } = string.Empty;
|
||||
public int MinimumSmsIntervalMinutes { get; set; }
|
||||
public int MinimumCallIntervalMinutes { get; set; }
|
||||
public decimal? AreaSquareMeters { get; set; }
|
||||
public int UploadIntervalMin { get; set; }
|
||||
public string DevicePhoneNumber { get; set; } = string.Empty;
|
||||
public Domain.SimCardType? SimCardType { get; set; }
|
||||
public int RelayCount { get; set; }
|
||||
public string SmsRecievers { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Success = !NotFound && Message == null
|
||||
/// </summary>
|
||||
public sealed class AdminSettingsUpdateResult
|
||||
{
|
||||
public bool NotFound { get; set; }
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AdminDeviceMemberDto
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Family { get; set; } = string.Empty;
|
||||
public string Mobile { get; set; } = string.Empty;
|
||||
public string MemberName { get; set; } = string.Empty;
|
||||
public string MemberFamily { get; set; } = string.Empty;
|
||||
public Domain.DeviceMemberRole Role { get; set; }
|
||||
public bool ReceiveAlerts { get; set; }
|
||||
public string? AvatarPath { get; set; }
|
||||
public bool IsOwner { get; set; }
|
||||
}
|
||||
|
||||
// ==================== Usage ====================
|
||||
|
||||
public sealed class UsageDayDto
|
||||
@@ -158,6 +234,10 @@ public sealed class AdminAlertItemDto
|
||||
public sealed class AdminPostFilter
|
||||
{
|
||||
public int? DeviceId { get; set; }
|
||||
public int? AuthorUserId { get; set; }
|
||||
public Domain.DevicePostType? PostType { get; set; }
|
||||
public DateTime? From { get; set; }
|
||||
public DateTime? To { get; set; }
|
||||
public int Page { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
@@ -167,13 +247,38 @@ public sealed class AdminPostItemDto
|
||||
public int Id { get; set; }
|
||||
public int DeviceId { get; set; }
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
public int AuthorUserId { get; set; }
|
||||
public string AuthorName { get; set; } = string.Empty;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public Domain.DevicePostType PostType { get; set; }
|
||||
public int ImageCount { get; set; }
|
||||
public bool HasVoice { get; set; }
|
||||
public int? VoiceDurationSeconds { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AdminPostDetailDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DeviceId { get; set; }
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
public string AuthorName { get; set; } = string.Empty;
|
||||
public string AuthorFamily { get; set; } = string.Empty;
|
||||
public Domain.DevicePostType PostType { get; set; }
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public IReadOnlyList<AdminPostImageDto> Images { get; set; } = [];
|
||||
public string? VoiceFilePath { get; set; }
|
||||
public int? VoiceDurationSeconds { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AdminPostImageDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FileName { get; set; } = string.Empty;
|
||||
public string FilePath { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// ==================== Device Debug ====================
|
||||
|
||||
public sealed class AdminDeviceTestResultDto
|
||||
@@ -182,6 +287,37 @@ public sealed class AdminDeviceTestResultDto
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// ==================== Reports ====================
|
||||
|
||||
public sealed class AdminUserGrowthRowDto
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public int NewUsers { get; set; }
|
||||
public int ActiveUsers { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AdminDeviceUptimeRowDto
|
||||
{
|
||||
public int DeviceId { get; set; }
|
||||
public string DeviceName { get; set; } = string.Empty;
|
||||
public string OwnerName { get; set; } = string.Empty;
|
||||
public int TotalDays { get; set; }
|
||||
public int OnlineDays { get; set; }
|
||||
public int OnlinePercent { get; set; }
|
||||
}
|
||||
|
||||
public sealed class AdminUsageByUserRowDto
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Family { get; set; } = string.Empty;
|
||||
public string Mobile { get; set; } = string.Empty;
|
||||
public int Sms { get; set; }
|
||||
public int VoiceCall { get; set; }
|
||||
public int AiQueries { get; set; }
|
||||
public int AiTokens { get; set; }
|
||||
}
|
||||
|
||||
// ==================== Audit Log ====================
|
||||
|
||||
public sealed class AdminAuditLogFilter
|
||||
|
||||
Reference in New Issue
Block a user