171 lines
5.9 KiB
C#
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; }
|
|
}
|