using Microsoft.AspNetCore.Http; using PlotLine.Models; namespace PlotLine.ViewModels; public sealed class ImportIndexViewModel { public string JsonText { get; set; } = string.Empty; public IFormFile? JsonFile { get; set; } public bool AllowDuplicateProjectTitle { get; set; } public bool ConfirmImport { get; set; } public bool AcknowledgeWarnings { get; set; } public ImportPreview? Preview { get; set; } public string? StatusMessage { get; set; } public string? TechnicalDetail { get; set; } public bool CanImport => Preview is { Validation.IsValid: true } && (!Preview.Validation.Warnings.Any() || AcknowledgeWarnings) && (!Preview.HasDuplicateProjectTitle || AllowDuplicateProjectTitle) && ConfirmImport; public static string SampleJson => """ { "packageVersion": "1.0", "source": "JSON Import Phase 1B sample", "project": { "title": "The Glass Meridian", "description": "A placeholder two-book fantasy outline.", "notes": "Imported sample data for testing." }, "books": [ { "title": "Book One", "subtitle": "The Map Beneath the City", "seriesOrder": 1, "dependsOnPreviousBook": false, "description": "The opening movement of the story.", "notes": "Keep this book focused on discovery.", "chapters": [ { "chapterNumber": 1, "title": "A Door in the Rain", "order": 1, "summary": "The protagonist finds a hidden route under the old district.", "notes": "Establish the central mystery.", "scenes": [ { "sceneNumber": 1, "title": "The Locked Arcade", "order": 1, "summary": "A late delivery leads to an impossible doorway.", "povCharacterName": "Mara", "locationName": "Old Arcade", "dateTimeText": "Rainy evening", "purposeText": "Introduce the mystery", "outcomeText": "Mara keeps the brass key", "notes": "Quiet, tense opening." }, { "sceneNumber": 2, "title": "Footsteps Below", "order": 2, "summary": "Mara hears someone moving beneath the street.", "povCharacterName": "Mara", "locationName": "Service stair", "dateTimeText": "Same evening", "purposeText": "Escalate the discovery", "outcomeText": "She chooses to descend", "notes": "End on a decision." } ] }, { "chapterNumber": 2, "title": "The Lantern Court", "order": 2, "summary": "The hidden route opens into a forgotten civic space.", "notes": "Add wonder, but keep danger near.", "scenes": [ { "sceneNumber": 1, "title": "A City Under Glass", "order": 1, "summary": "Mara sees the underground court for the first time.", "povCharacterName": "Mara", "locationName": "Lantern Court", "dateTimeText": "Later that night", "purposeText": "Reveal the hidden setting", "outcomeText": "Mara realizes the map is incomplete", "notes": "Use strong visual contrast." }, { "sceneNumber": 2, "title": "The Watchman's Offer", "order": 2, "summary": "A stranger offers help at a cost.", "povCharacterName": "Mara", "locationName": "Lantern Court", "dateTimeText": "Later that night", "purposeText": "Introduce an uneasy ally", "outcomeText": "Mara accepts a temporary guide", "notes": "Trust should feel provisional." } ] } ] }, { "title": "Book Two", "subtitle": "The Clockwork Shore", "seriesOrder": 2, "dependsOnPreviousBook": true, "description": "The story widens beyond the city.", "notes": "Seed consequences from book one.", "chapters": [ { "chapterNumber": 1, "title": "Tide Tables", "order": 1, "summary": "The route points toward a mechanical coastline.", "notes": "Reorient the cast after the first book.", "scenes": [ { "sceneNumber": 1, "title": "Departure Bell", "order": 1, "summary": "The expedition leaves before dawn.", "povCharacterName": "Ilen", "locationName": "East Station", "dateTimeText": "Early morning", "purposeText": "Start the next journey", "outcomeText": "The group leaves the city", "notes": "Keep momentum high." }, { "sceneNumber": 2, "title": "Salt in the Gears", "order": 2, "summary": "The first coastal machine fails in a revealing way.", "povCharacterName": "Ilen", "locationName": "Clockwork Shore", "dateTimeText": "Midday", "purposeText": "Show the new world's rules", "outcomeText": "They find a damaged signal plate", "notes": "Make the mechanism readable." } ] }, { "chapterNumber": 2, "title": "The Signal Plate", "order": 2, "summary": "The signal points to a rival expedition.", "notes": "Bring in pressure from outside the group.", "scenes": [ { "sceneNumber": 1, "title": "A Message in Brass", "order": 1, "summary": "The plate reveals a warning.", "povCharacterName": "Mara", "locationName": "Shore workshop", "dateTimeText": "Afternoon", "purposeText": "Translate the clue", "outcomeText": "The warning names a missing captain", "notes": "Make the clue actionable." }, { "sceneNumber": 2, "title": "Smoke on the Causeway", "order": 2, "summary": "A rival camp appears across the water.", "povCharacterName": "Mara", "locationName": "Tidal causeway", "dateTimeText": "Sunset", "purposeText": "Introduce opposition", "outcomeText": "The group prepares to cross", "notes": "End with forward motion." } ] } ] } ] } """; }