PlotDirector/PlotLine/Models/FeatureRequestModels.cs

171 lines
5.9 KiB
C#

namespace PlotLine.Models;
public static class FeatureRequestStatuses
{
public const string New = "New";
public const string NeedsDetail = "Needs Detail";
public const string UnderReview = "Under Review";
public const string Candidate = "Candidate";
public const string Planned = "Planned";
public const string InProgress = "In Progress";
public const string Shipped = "Shipped";
public const string NotPlanned = "Not Planned";
public static IReadOnlyList<string> All { get; } =
[
New,
NeedsDetail,
UnderReview,
Candidate,
Planned,
InProgress,
Shipped,
NotPlanned
];
public static bool IsValid(string? status) => All.Contains(status ?? string.Empty, StringComparer.OrdinalIgnoreCase);
public static bool IsOpen(string? status) => !string.Equals(status, Shipped, StringComparison.OrdinalIgnoreCase)
&& !string.Equals(status, NotPlanned, StringComparison.OrdinalIgnoreCase);
public static string Explanation(string? status) => status switch
{
New => "Your suggestion has been received and is waiting for review.",
NeedsDetail => "We need a little more information before we can evaluate this suggestion.",
UnderReview => "We are currently evaluating this idea.",
Candidate => "This suggestion is being considered for a future release.",
Planned => "This suggestion has been accepted and is planned for development.",
InProgress => "Work has started on this feature.",
Shipped => "This feature has been completed and released.",
NotPlanned => "This suggestion does not currently fit PlotDirector's roadmap.",
_ => "This suggestion is being reviewed."
};
}
public static class FeatureRequestSortOptions
{
public const string LatestActivity = "LatestActivity";
public const string NewestFirst = "NewestFirst";
public const string OldestFirst = "OldestFirst";
public static IReadOnlyList<(string Value, string Label)> All { get; } =
[
(LatestActivity, "Latest activity"),
(NewestFirst, "Newest first"),
(OldestFirst, "Oldest first")
];
public static bool IsValid(string? sort) => All.Any(option => string.Equals(option.Value, sort, StringComparison.OrdinalIgnoreCase));
}
public static class FeatureRequestOptions
{
public static IReadOnlyList<string> AppAreas { get; } =
[
"Timeline",
"Characters",
"Locations",
"Assets",
"Plot Lines",
"Continuity",
"Writing Schedule",
"Word Companion",
"Import / Export",
"Billing / Account",
"Other"
];
public static IReadOnlyList<string> ImportanceOptions { get; } =
[
"Nice to have",
"Would save me time",
"Blocking my workflow"
];
}
public sealed class FeatureRequest
{
public int FeatureRequestID { get; set; }
public int UserID { get; set; }
public string? UserEmail { get; set; }
public string? UserDisplayName { get; set; }
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string? AppArea { get; set; }
public string? Importance { get; set; }
public string Status { get; set; } = FeatureRequestStatuses.New;
public bool IsPublicCandidate { get; set; }
public string? AdminNotes { get; set; }
public DateTime CreatedUtc { get; set; }
public DateTime UpdatedUtc { get; set; }
public DateTime? ClosedUtc { get; set; }
public DateTime? ShippedUtc { get; set; }
public DateTime? LatestActivityUtc { get; set; }
public bool? LatestMessageIsAdmin { get; set; }
public string? LatestMessagePreview { get; set; }
public int MessageCount { get; set; }
public DateTime? UserLastViewedUtc { get; set; }
public DateTime? UserAttentionUtc { get; set; }
public bool UserNeedsAttention { get; set; }
public bool IsOpen => FeatureRequestStatuses.IsOpen(Status);
public string StatusExplanation => FeatureRequestStatuses.Explanation(Status);
public string ConversationState
{
get
{
if ((LatestMessageIsAdmin is null || LatestMessageIsAdmin == false) && IsOpen)
{
return FeatureRequestConversationStates.WaitingForAdminReply;
}
if (LatestMessageIsAdmin == true && string.Equals(Status, FeatureRequestStatuses.NeedsDetail, StringComparison.OrdinalIgnoreCase))
{
return FeatureRequestConversationStates.WaitingForUserReply;
}
return FeatureRequestConversationStates.NoActionNeeded;
}
}
}
public sealed class FeatureRequestRoadmapSection
{
public string Heading { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public IReadOnlyList<FeatureRequest> Requests { get; set; } = [];
}
public static class FeatureRequestConversationStates
{
public const string WaitingForAdminReply = "Waiting for Admin Reply";
public const string WaitingForUserReply = "Waiting for User Reply";
public const string NoActionNeeded = "Up to date";
}
public sealed class FeatureRequestAdminSummary
{
public IReadOnlyList<FeatureRequestStatusSummary> Statuses { get; set; } = [];
public int TotalOpenRequests { get; set; }
public int AwaitingAdminReply { get; set; }
public int AwaitingUserReply { get; set; }
}
public sealed class FeatureRequestStatusSummary
{
public string Status { get; set; } = string.Empty;
public int Count { get; set; }
}
public sealed class FeatureRequestMessage
{
public int FeatureRequestMessageID { get; set; }
public int FeatureRequestID { get; set; }
public int? UserID { get; set; }
public string? UserEmail { get; set; }
public string? UserDisplayName { get; set; }
public bool IsAdmin { get; set; }
public string MessageBody { get; set; } = string.Empty;
public DateTime CreatedUtc { get; set; }
}