feat(ideas): add idea creator, grant calls and applications
Implement the idea creator and application generator: idea status workflow, social canvas card, grant call windows with 409 when closed, and an LLM-backed idea assistant with offline fallback.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using TarninaHub.Application.Dtos;
|
||||
|
||||
namespace TarninaHub.Application.Abstractions;
|
||||
|
||||
public interface IIdeaService
|
||||
{
|
||||
Task<IdeaResponse> CreateAsync(string authorId, CreateIdeaRequest request, CancellationToken cancellationToken);
|
||||
|
||||
Task<IdeaResponse?> GetAsync(string ideaId, CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<IdeaResponse>> ListForAuthorAsync(string authorId, CancellationToken cancellationToken);
|
||||
|
||||
Task<IdeaResponse?> UpdateAsync(string ideaId, UpdateIdeaRequest request, CancellationToken cancellationToken);
|
||||
|
||||
Task<IdeaCardResponse?> GetCardAsync(string ideaId, CancellationToken cancellationToken);
|
||||
|
||||
Task<IdeaCardResponse?> UpdateCardAsync(string ideaId, IdeaCardRequest request, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IIdeaAssistant
|
||||
{
|
||||
Task<IdeaAssistResponse?> AssistAsync(
|
||||
string ideaId,
|
||||
IdeaAssistRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IGrantCallService
|
||||
{
|
||||
Task<IReadOnlyList<GrantCallResponse>> ListAsync(string? status, CancellationToken cancellationToken);
|
||||
|
||||
Task<GrantCallResponse?> GetAsync(string grantCallId, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public enum CreateApplicationOutcome
|
||||
{
|
||||
Created,
|
||||
CallNotFound,
|
||||
CallNotOpen,
|
||||
IdeaNotFound,
|
||||
}
|
||||
|
||||
public sealed record CreateApplicationResult(
|
||||
CreateApplicationOutcome Outcome,
|
||||
ApplicationResponse? Application);
|
||||
|
||||
public interface IApplicationService
|
||||
{
|
||||
Task<CreateApplicationResult> CreateAsync(
|
||||
string userId,
|
||||
CreateApplicationRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<ApplicationResponse?> GetAsync(string applicationId, CancellationToken cancellationToken);
|
||||
|
||||
Task<IReadOnlyList<ApplicationResponse>> ListForAuthorAsync(
|
||||
string authorId,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TarninaHub.Application.Dtos;
|
||||
|
||||
public sealed record GrantCallResponse(
|
||||
string Id,
|
||||
string Name,
|
||||
DateTimeOffset OpensAt,
|
||||
DateTimeOffset ClosesAt,
|
||||
string Status,
|
||||
string? ExternalId);
|
||||
|
||||
public sealed record CreateApplicationRequest(
|
||||
[Required] string GrantCallId,
|
||||
[Required] string IdeaId,
|
||||
IReadOnlyDictionary<string, object?>? Content);
|
||||
|
||||
public sealed record UpdateApplicationRequest(
|
||||
IReadOnlyDictionary<string, object?>? Content,
|
||||
string? Status);
|
||||
|
||||
public sealed record ApplicationResponse(
|
||||
string Id,
|
||||
string GrantCallId,
|
||||
string IdeaId,
|
||||
IReadOnlyDictionary<string, object?> Content,
|
||||
string Status,
|
||||
DateTimeOffset CreatedAt);
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace TarninaHub.Application.Dtos;
|
||||
|
||||
public sealed record IdeaCardRequest(
|
||||
string? Essence,
|
||||
string? Audience,
|
||||
string? Stage);
|
||||
|
||||
public sealed record CreateIdeaRequest(
|
||||
[Required, StringLength(200, MinimumLength = 3)] string Title,
|
||||
[Required, StringLength(4000, MinimumLength = 10)] string Description,
|
||||
IdeaCardRequest? Card);
|
||||
|
||||
public sealed record UpdateIdeaRequest(
|
||||
[Required, StringLength(200, MinimumLength = 3)] string Title,
|
||||
[Required, StringLength(4000, MinimumLength = 10)] string Description,
|
||||
string? Status);
|
||||
|
||||
public sealed record IdeaCardResponse(
|
||||
string Id,
|
||||
string IdeaId,
|
||||
string Essence,
|
||||
string Audience,
|
||||
string Stage);
|
||||
|
||||
public sealed record IdeaResponse(
|
||||
string Id,
|
||||
string Title,
|
||||
string Description,
|
||||
string AuthorId,
|
||||
string Status,
|
||||
IdeaCardResponse? Card,
|
||||
DateTimeOffset? UpdatedAt);
|
||||
|
||||
public sealed record IdeaAssistRequest(string? Focus);
|
||||
|
||||
public sealed record IdeaAssistResponse(
|
||||
IReadOnlyList<string> Suggestions,
|
||||
string ImprovedDescription,
|
||||
bool FromLlm);
|
||||
@@ -0,0 +1,26 @@
|
||||
using TarninaHub.Domain.Enums;
|
||||
|
||||
namespace TarninaHub.Application.Mappings;
|
||||
|
||||
public static class IdeaStatusWire
|
||||
{
|
||||
public static string ToWire(this IdeaStatus status) => status switch
|
||||
{
|
||||
IdeaStatus.Draft => "draft",
|
||||
IdeaStatus.Submitted => "submitted",
|
||||
IdeaStatus.InReview => "in_review",
|
||||
IdeaStatus.Accepted => "accepted",
|
||||
IdeaStatus.Rejected => "rejected",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null),
|
||||
};
|
||||
|
||||
public static IdeaStatus? Parse(string? value) => value switch
|
||||
{
|
||||
"draft" => IdeaStatus.Draft,
|
||||
"submitted" => IdeaStatus.Submitted,
|
||||
"in_review" => IdeaStatus.InReview,
|
||||
"accepted" => IdeaStatus.Accepted,
|
||||
"rejected" => IdeaStatus.Rejected,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user