PlotDirector/PlotLine/ViewModels/ImportViewModels.cs
2026-06-01 20:47:20 +01:00

181 lines
7.0 KiB
C#

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 ImportPreview? Preview { get; set; }
public string? StatusMessage { get; set; }
public bool CanImport => Preview is { Validation.IsValid: true } && (!Preview.HasDuplicateProjectTitle || AllowDuplicateProjectTitle);
public static string SampleJson => """
{
"packageVersion": "1.0",
"source": "JSON Import Phase 1A 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."
}
]
}
]
}
]
}
""";
}