using System.ComponentModel.DataAnnotations; namespace GerbilManagerWebAPI.Models { /// /// FEEDBACK: a user-submitted "Fehler melden" report. Decoupled from the rest of the /// model on purpose — GerbilId/LitterId are plain nullable Guid columns (NOT enforced /// foreign keys), so the import re-ingest wipe (IngestResolvedService) can delete /// gerbils/litters without deleting or breaking feedback rows. The captured EntityName /// keeps the report human-readable even after the referenced animal is gone. /// public class Feedback { [Key] public Guid Id { get; set; } /// The user's free-text description of the problem. public required string Message { get; set; } /// Which view the report came from: stammbaum | gerbil-detail | litter-detail. public required string Context { get; set; } /// Loose reference (no FK) to the gerbil the report is about, if any. public Guid? GerbilId { get; set; } /// Loose reference (no FK) to the litter the report is about, if any. public Guid? LitterId { get; set; } /// Loose reference (no FK) to the contact the report is about, if any. public Guid? ContactId { get; set; } /// Captured name of the referenced animal/litter (survives an ingest wipe). public string? EntityName { get; set; } /// The client URL/route the report was filed from. public string? Url { get; set; } /// Client-supplied timestamp (when the user submitted, in their browser). public DateTimeOffset? ClientTimestamp { get; set; } /// Optional browser user-agent for diagnostics. public string? UserAgent { get; set; } /// Server-side creation time. public DateTimeOffset CreatedAt { get; set; } /// /// Ticket lifecycle status: "Open" | "NeedsInfo" | "Answered" | "Resolved" (default "Open"). /// "NeedsInfo" = a maintainer attached a clarifying question (Rückfrage) and is waiting on /// the breeder; "Answered" = the breeder replied. Plain string, no FK — keeps feedback /// decoupled and ingest-surviving like the rest of the row. /// public string Status { get; set; } = "Open"; /// When the ticket was marked resolved; null while open. public DateTimeOffset? ResolvedAt { get; set; } /// /// When the ticket was reopened FROM a resolved state that had no pending Rückfrage — /// i.e. a genuinely-fixed ticket the breeder wants reworked. Stays null for tickets that /// merely had an open question, got marked resolved, and were reopened (those just return /// to their existing Rückfrage). Shown as "Wieder geöffnet am" in the UI. /// public DateTimeOffset? ReopenedAt { get; set; } /// /// SOFT-DELETE: when the breeder deleted the ticket via the UI. Soft-deleted tickets are /// NOT removed from the DB — they move into the "Gelöscht" category and can be restored /// (DeletedAt → null) from there. null = not deleted. /// public DateTimeOffset? DeletedAt { get; set; } /// A clarifying question (Rückfrage) a maintainer attaches to the ticket; null if none. public string? Question { get; set; } /// The breeder's (Züchterin) reply to the clarifying question; null until answered. public string? Answer { get; set; } /// When the breeder answered the clarifying question; null until answered. public DateTimeOffset? AnsweredAt { get; set; } /// /// FIX-NOTE: a breeder-friendly changelog written when the ticket is resolved — plain /// language ("was sich für SIE sichtbar geändert hat"), no technical jargon. Shown in the /// "Geschlossen" view. null while the ticket is open / unresolved. /// public string? FixNote { get; set; } /// /// AGENT-CONTEXT (INTERNAL): the maintainer/agent's working memory — findings, suspected /// cause, files/data touched, the plan, what it is waiting on. MAY contain ids/filenames. /// Returned by the DTO for tooling, but NEVER rendered in the breeder UI. null if unused. /// public string? AgentContext { get; set; } /// /// THREAD/HISTORY: a JSON array of past Q&A turns so earlier rounds aren't lost when a new /// question is asked. Each entry: { "role": "maintainer" | "breeder", "text": string, /// "at": ISO-8601 string }. The current open exchange stays in Question/Answer; when a NEW /// question is set while a previous Q&A exists, the previous (question, answer) is appended /// here first. Plain string column (no FK) so it survives the ingest wipe. null/empty = no history. /// public string? Thread { get; set; } } }