Merged
This commit is contained in:
commit
0f7b6a8116
332
PlotLine/Docs/Features/Phase-20-Story-Intelligence-Onboarding.md
Normal file
332
PlotLine/Docs/Features/Phase-20-Story-Intelligence-Onboarding.md
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
# Phase 20 -- Story Intelligence Onboarding
|
||||||
|
|
||||||
|
## Functional Design Specification
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
The purpose of the Story Intelligence Onboarding Wizard is to transform
|
||||||
|
a new user's manuscript into a structured PlotDirector project with as
|
||||||
|
little manual effort as possible.
|
||||||
|
|
||||||
|
Rather than expecting the user to manually create chapters, scenes,
|
||||||
|
characters and summaries, PlotDirector will guide them through an
|
||||||
|
onboarding process appropriate to the way they write.
|
||||||
|
|
||||||
|
The wizard has two distinct objectives:
|
||||||
|
|
||||||
|
1. Remove the "blank project" experience that new users currently
|
||||||
|
encounter.
|
||||||
|
2. Demonstrate PlotDirector's capabilities immediately by building a
|
||||||
|
rich story model from the user's existing manuscript.
|
||||||
|
|
||||||
|
The wizard is website-led throughout. The PlotDirector Word Companion
|
||||||
|
acts only as a document scanner and synchronisation agent where
|
||||||
|
Microsoft Word is used.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Design Principles
|
||||||
|
|
||||||
|
- The onboarding should feel like a conversation rather than a
|
||||||
|
configuration screen.
|
||||||
|
- The user should never be presented with options that are irrelevant
|
||||||
|
to the software they use.
|
||||||
|
- AI is entirely optional.
|
||||||
|
- The user always remains in control.
|
||||||
|
- No manuscript is analysed using AI without explicit consent.
|
||||||
|
- The Word Companion remains the preferred workflow but is never
|
||||||
|
mandatory.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Wizard Flow
|
||||||
|
|
||||||
|
## Step 1 -- Welcome
|
||||||
|
|
||||||
|
Display a friendly welcome explaining that PlotDirector will tailor the
|
||||||
|
setup to the author's workflow.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Step 2 -- Where are you in your writing journey?
|
||||||
|
|
||||||
|
- I'm planning a brand new story
|
||||||
|
- I've already started writing
|
||||||
|
- My manuscript is largely complete
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Step 3 -- What software do you write in?
|
||||||
|
|
||||||
|
- Microsoft Word (Recommended)
|
||||||
|
- Google Docs
|
||||||
|
- Scrivener
|
||||||
|
- LibreOffice / OpenOffice
|
||||||
|
- Markdown / Obsidian
|
||||||
|
- Other
|
||||||
|
|
||||||
|
Store this preference against the user profile.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Step 4 -- Create your first project
|
||||||
|
|
||||||
|
Collect:
|
||||||
|
|
||||||
|
- Project Name
|
||||||
|
- Optional Description
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Step 5 -- Create your first book
|
||||||
|
|
||||||
|
Collect:
|
||||||
|
|
||||||
|
- Book Title
|
||||||
|
- Subtitle
|
||||||
|
- Genre
|
||||||
|
- Estimated Word Count (optional)
|
||||||
|
- Cover Image (optional)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Path A -- Microsoft Word
|
||||||
|
|
||||||
|
## A1 -- Install the Word Companion
|
||||||
|
|
||||||
|
Provide:
|
||||||
|
|
||||||
|
- Download button
|
||||||
|
- Installation guide
|
||||||
|
- Short demonstration video
|
||||||
|
- Troubleshooting link
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A2 -- Open the manuscript
|
||||||
|
|
||||||
|
The user opens the manuscript in Word, starts the Companion, signs in
|
||||||
|
and links the document to the selected Project and Book.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A3 -- Companion Detection
|
||||||
|
|
||||||
|
The Companion silently registers itself with PlotDirector, including:
|
||||||
|
|
||||||
|
- User
|
||||||
|
- Companion Version
|
||||||
|
- Current Document
|
||||||
|
- Linked Project
|
||||||
|
- Linked Book
|
||||||
|
- SignalR Session
|
||||||
|
|
||||||
|
The website updates immediately to show **Word Companion Connected**.
|
||||||
|
|
||||||
|
Until this point the Companion status is hidden from the application
|
||||||
|
header.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A4 -- Scan Manuscript
|
||||||
|
|
||||||
|
The website instructs the Companion (via SignalR) to scan the document.
|
||||||
|
|
||||||
|
The Companion detects:
|
||||||
|
|
||||||
|
- Chapters
|
||||||
|
- Scenes
|
||||||
|
- Word Counts
|
||||||
|
- Character Candidates
|
||||||
|
- Hidden Document IDs
|
||||||
|
- Document Metadata
|
||||||
|
|
||||||
|
Progress is streamed continuously back to PlotDirector.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A5 -- Review Scan
|
||||||
|
|
||||||
|
Display the detected:
|
||||||
|
|
||||||
|
- Chapters
|
||||||
|
- Scenes
|
||||||
|
- Character Suggestions
|
||||||
|
|
||||||
|
Allow editing before import.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A6 -- Import Structure
|
||||||
|
|
||||||
|
Create:
|
||||||
|
|
||||||
|
- Chapters
|
||||||
|
- Scenes
|
||||||
|
- Characters
|
||||||
|
- Initial Scene Metadata
|
||||||
|
|
||||||
|
Return created IDs to the Companion so hidden document markers can be
|
||||||
|
updated.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## A7 -- Story Intelligence (Optional)
|
||||||
|
|
||||||
|
Explain that PlotDirector can analyse the manuscript using AI.
|
||||||
|
|
||||||
|
Explain:
|
||||||
|
|
||||||
|
- Entirely optional.
|
||||||
|
- Manuscript is securely processed using the OpenAI API.
|
||||||
|
- API content is not used to train AI models.
|
||||||
|
- Show estimated time.
|
||||||
|
- Show cost or included credits.
|
||||||
|
|
||||||
|
Buttons:
|
||||||
|
|
||||||
|
- Analyse Now
|
||||||
|
- Skip
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Path B -- Other Writing Software
|
||||||
|
|
||||||
|
## B1 -- Export
|
||||||
|
|
||||||
|
Guide the user to export their manuscript as **.docx**.
|
||||||
|
|
||||||
|
## B2 -- Upload
|
||||||
|
|
||||||
|
Upload the document.
|
||||||
|
|
||||||
|
Use the same scanning engine as the Word Companion.
|
||||||
|
|
||||||
|
## B3 -- Review
|
||||||
|
|
||||||
|
Display detected structure.
|
||||||
|
|
||||||
|
## B4 -- Import
|
||||||
|
|
||||||
|
Create the project structure.
|
||||||
|
|
||||||
|
Explain that future updates can be imported by uploading a fresh Word
|
||||||
|
export.
|
||||||
|
|
||||||
|
## B5 -- Story Intelligence
|
||||||
|
|
||||||
|
Offer the same optional AI analysis.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Story Intelligence Pipeline
|
||||||
|
|
||||||
|
The pipeline should progressively build a structured Story Model.
|
||||||
|
|
||||||
|
1. Scene analysis
|
||||||
|
2. Character profiles
|
||||||
|
3. Relationship summaries
|
||||||
|
4. Location summaries
|
||||||
|
5. Asset summaries
|
||||||
|
6. Plot thread suggestions
|
||||||
|
7. Timeline observations
|
||||||
|
8. Continuity and warning generation
|
||||||
|
|
||||||
|
Each stage should consume structured data from earlier stages wherever
|
||||||
|
possible rather than repeatedly analysing the manuscript.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Progress Experience
|
||||||
|
|
||||||
|
Progress should be streamed live using SignalR.
|
||||||
|
|
||||||
|
Example messages:
|
||||||
|
|
||||||
|
- Preparing manuscript
|
||||||
|
- Reading Chapter 12 of 38
|
||||||
|
- Summarising Scene 54
|
||||||
|
- Identifying recurring characters
|
||||||
|
- Building relationship map
|
||||||
|
- Finding important locations
|
||||||
|
- Tracking story assets
|
||||||
|
- Constructing timeline
|
||||||
|
- Generating character biographies
|
||||||
|
- Building your Story Model
|
||||||
|
- Finalising project
|
||||||
|
|
||||||
|
Display live counters for:
|
||||||
|
|
||||||
|
- Chapters
|
||||||
|
- Scenes
|
||||||
|
- Characters
|
||||||
|
- Locations
|
||||||
|
- Assets
|
||||||
|
- Relationships
|
||||||
|
- Plot Threads
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Completion Screen
|
||||||
|
|
||||||
|
Present a dedicated completion page.
|
||||||
|
|
||||||
|
Example summary:
|
||||||
|
|
||||||
|
- 42 Chapters
|
||||||
|
- 186 Scenes
|
||||||
|
- 38 Characters
|
||||||
|
- 24 Locations
|
||||||
|
- 18 Assets
|
||||||
|
- 31 Relationships
|
||||||
|
- 11 Plot Threads
|
||||||
|
- 315 AI Suggestions
|
||||||
|
|
||||||
|
Buttons:
|
||||||
|
|
||||||
|
- Review AI Suggestions
|
||||||
|
- Open Project Overview
|
||||||
|
- Open Writer Workspace
|
||||||
|
- Take a Quick Tour
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Dashboard Behaviour
|
||||||
|
|
||||||
|
If Microsoft Word was selected but the Companion has never connected:
|
||||||
|
|
||||||
|
Display a dashboard reminder.
|
||||||
|
|
||||||
|
Once connected:
|
||||||
|
|
||||||
|
- Hide installation prompts.
|
||||||
|
- Show Companion status in the application header.
|
||||||
|
- Future connections occur silently.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Future Enhancements
|
||||||
|
|
||||||
|
- Automatic manuscript update detection.
|
||||||
|
- Incremental re-analysis.
|
||||||
|
- Selective AI regeneration.
|
||||||
|
- AI confidence scores.
|
||||||
|
- Author approval queue.
|
||||||
|
- Background processing.
|
||||||
|
- Analysis history.
|
||||||
|
- Version comparison.
|
||||||
|
- Additional writing platform support.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Guiding Philosophy
|
||||||
|
|
||||||
|
The onboarding wizard is not simply importing a manuscript.
|
||||||
|
|
||||||
|
It is constructing a structured **Story Model** that powers
|
||||||
|
PlotDirector's timelines, continuity analysis, relationships, locations,
|
||||||
|
assets, plot threads, reports and writing tools.
|
||||||
|
|
||||||
|
The end result should leave authors feeling that PlotDirector has
|
||||||
|
genuinely understood their story rather than simply copied their
|
||||||
|
document into a database.
|
||||||
@ -16,6 +16,7 @@ public sealed class WordCompanionBookDto
|
|||||||
public int BookId { get; set; }
|
public int BookId { get; set; }
|
||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
public string? Subtitle { get; set; }
|
public string? Subtitle { get; set; }
|
||||||
|
public string DisplayTitle => BookTitleFormatter.DisplayTitle(Title, Subtitle);
|
||||||
public int SortOrder { get; set; }
|
public int SortOrder { get; set; }
|
||||||
public bool UsesExplicitScenes { get; set; }
|
public bool UsesExplicitScenes { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
244
PlotLine/Sql/109_DropdownListOrderingConsistency.sql
Normal file
244
PlotLine/Sql/109_DropdownListOrderingConsistency.sql
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
SET ANSI_NULLS ON;
|
||||||
|
GO
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.StoryAsset_ListByProject
|
||||||
|
@ProjectID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
CREATE TABLE #ProjectLocationPaths
|
||||||
|
(
|
||||||
|
LocationID int NOT NULL PRIMARY KEY,
|
||||||
|
LocationName nvarchar(200) NOT NULL,
|
||||||
|
LocationPath nvarchar(max) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
;WITH ProjectLocationTree AS
|
||||||
|
(
|
||||||
|
SELECT LocationID, LocationName, CAST(LocationName AS nvarchar(max)) AS LocationPath
|
||||||
|
FROM dbo.Locations
|
||||||
|
WHERE ProjectID = @ProjectID AND ParentLocationID IS NULL AND IsArchived = 0
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT child.LocationID, child.LocationName, CAST(parent.LocationPath + N' > ' + child.LocationName AS nvarchar(max)) AS LocationPath
|
||||||
|
FROM dbo.Locations child
|
||||||
|
INNER JOIN ProjectLocationTree parent ON parent.LocationID = child.ParentLocationID
|
||||||
|
WHERE child.ProjectID = @ProjectID AND child.IsArchived = 0
|
||||||
|
)
|
||||||
|
INSERT #ProjectLocationPaths (LocationID, LocationName, LocationPath)
|
||||||
|
SELECT LocationID, LocationName, LocationPath
|
||||||
|
FROM ProjectLocationTree;
|
||||||
|
|
||||||
|
SELECT sa.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName, sa.Description, sa.Importance,
|
||||||
|
sa.CurrentStateID, ast.StateName AS CurrentStateName, sa.CurrentLocationID,
|
||||||
|
location.LocationName AS CurrentLocationName, location.LocationPath AS CurrentLocationPath,
|
||||||
|
sa.IsResolved, sa.ShowInQuickAddBar, sa.ImagePath, sa.ThumbnailPath, sa.CreatedDate, sa.UpdatedDate, sa.IsArchived
|
||||||
|
FROM dbo.StoryAssets sa
|
||||||
|
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
|
||||||
|
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
|
||||||
|
LEFT JOIN #ProjectLocationPaths location ON location.LocationID = sa.CurrentLocationID
|
||||||
|
WHERE sa.ProjectID = @ProjectID
|
||||||
|
AND sa.IsArchived = 0
|
||||||
|
ORDER BY sa.AssetName, sa.StoryAssetID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.AssetTimeline_GetByProject
|
||||||
|
@ProjectID int,
|
||||||
|
@BookID int = NULL
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT sa.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName, sa.Description, sa.Importance,
|
||||||
|
sa.CurrentStateID, ast.StateName AS CurrentStateName, sa.CurrentLocationID, sa.IsResolved,
|
||||||
|
sa.ShowInQuickAddBar, sa.CreatedDate, sa.UpdatedDate, sa.IsArchived
|
||||||
|
FROM dbo.StoryAssets sa
|
||||||
|
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
|
||||||
|
LEFT JOIN dbo.AssetStates ast ON ast.AssetStateID = sa.CurrentStateID
|
||||||
|
WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND (sa.Importance >= 5 OR sa.IsResolved = 0)
|
||||||
|
ORDER BY sa.AssetName, sa.StoryAssetID;
|
||||||
|
|
||||||
|
SELECT ae.AssetEventID, ae.StoryAssetID, sa.ProjectID, sa.AssetName, sa.AssetKindID, ak.KindName,
|
||||||
|
ae.SceneID, ae.AssetEventTypeID, aet.TypeName AS AssetEventTypeName, aet.MarkerText,
|
||||||
|
ae.FromStateID, fs.StateName AS FromStateName, ae.ToStateID, ts.StateName AS ToStateName,
|
||||||
|
ae.EventTitle, ae.EventDescription, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||||
|
c.ChapterNumber, s.SceneNumber, s.SceneTitle, ae.CreatedDate, ae.UpdatedDate
|
||||||
|
FROM dbo.AssetEvents ae
|
||||||
|
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID
|
||||||
|
INNER JOIN dbo.AssetKinds ak ON ak.AssetKindID = sa.AssetKindID
|
||||||
|
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
|
||||||
|
INNER JOIN dbo.Scenes s ON s.SceneID = ae.SceneID
|
||||||
|
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||||
|
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||||
|
LEFT JOIN dbo.AssetStates fs ON fs.AssetStateID = ae.FromStateID
|
||||||
|
LEFT JOIN dbo.AssetStates ts ON ts.AssetStateID = ae.ToStateID
|
||||||
|
WHERE sa.ProjectID = @ProjectID AND sa.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID)
|
||||||
|
ORDER BY b.SortOrder, b.BookNumber, c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, sa.AssetName, ae.AssetEventID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.Location_ListByProject
|
||||||
|
@ProjectID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT lp.LocationID, lp.ProjectID, lp.ParentLocationID, lp.LocationName, lp.LocationTypeID, lt.TypeName AS LocationTypeName,
|
||||||
|
lp.Description, lp.ShowInQuickAddBar, lp.ExcludeFromCompanionDetection, lp.DetectionPriority,
|
||||||
|
lp.LocationPath, lp.Depth, l.CreatedDate, l.UpdatedDate, l.IsArchived
|
||||||
|
FROM dbo.LocationPaths lp
|
||||||
|
INNER JOIN dbo.Locations l ON l.LocationID = lp.LocationID
|
||||||
|
LEFT JOIN dbo.LocationTypes lt ON lt.LocationTypeID = lp.LocationTypeID
|
||||||
|
WHERE lp.ProjectID = @ProjectID
|
||||||
|
ORDER BY lp.LocationPath, lp.LocationID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.PlotThread_ListByProject
|
||||||
|
@ProjectID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT pt.PlotThreadID, pt.PlotLineID, pl.ProjectID, pl.PlotLineName, pt.ThreadTitle,
|
||||||
|
pt.ThreadTypeID, tt.TypeName AS ThreadTypeName, pt.ThreadStatusID, ts.StatusName AS ThreadStatusName,
|
||||||
|
pt.Importance, pt.Summary, pt.IntroducedSceneID, pt.PlannedResolutionSceneID, pt.ActualResolutionSceneID,
|
||||||
|
pt.CreatedDate, pt.UpdatedDate, pt.IsArchived
|
||||||
|
FROM dbo.PlotThreads pt
|
||||||
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||||
|
INNER JOIN dbo.ThreadTypes tt ON tt.ThreadTypeID = pt.ThreadTypeID
|
||||||
|
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||||
|
WHERE pl.ProjectID = @ProjectID AND pl.IsArchived = 0 AND pt.IsArchived = 0
|
||||||
|
ORDER BY pt.ThreadTitle, pl.PlotLineName, pt.PlotThreadID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.PlotThread_ListByPlotLine
|
||||||
|
@PlotLineID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT pt.PlotThreadID, pt.PlotLineID, pl.ProjectID, pl.PlotLineName, pt.ThreadTitle,
|
||||||
|
pt.ThreadTypeID, tt.TypeName AS ThreadTypeName, pt.ThreadStatusID, ts.StatusName AS ThreadStatusName,
|
||||||
|
pt.Importance, pt.Summary, pt.IntroducedSceneID, pt.PlannedResolutionSceneID, pt.ActualResolutionSceneID,
|
||||||
|
pt.CreatedDate, pt.UpdatedDate, pt.IsArchived
|
||||||
|
FROM dbo.PlotThreads pt
|
||||||
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||||
|
INNER JOIN dbo.ThreadTypes tt ON tt.ThreadTypeID = pt.ThreadTypeID
|
||||||
|
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||||
|
WHERE pt.PlotLineID = @PlotLineID AND pt.IsArchived = 0
|
||||||
|
ORDER BY pt.ThreadTitle, pt.PlotThreadID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.WordCompanion_Book_ListByProject
|
||||||
|
@ProjectID int,
|
||||||
|
@UserID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.ProjectUserAccess pua
|
||||||
|
INNER JOIN dbo.Projects p ON p.ProjectID = pua.ProjectID
|
||||||
|
WHERE pua.ProjectID = @ProjectID
|
||||||
|
AND pua.UserID = @UserID
|
||||||
|
AND pua.IsActive = 1
|
||||||
|
AND p.IsArchived = 0
|
||||||
|
)
|
||||||
|
RETURN;
|
||||||
|
|
||||||
|
SELECT b.BookID AS BookId,
|
||||||
|
b.BookTitle AS Title,
|
||||||
|
b.Subtitle,
|
||||||
|
b.SortOrder,
|
||||||
|
b.UsesExplicitScenes
|
||||||
|
FROM dbo.Books b
|
||||||
|
WHERE b.ProjectID = @ProjectID
|
||||||
|
AND b.IsArchived = 0
|
||||||
|
ORDER BY b.SortOrder, b.BookNumber, b.BookTitle, b.Subtitle, b.BookID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.WordCompanion_Book_Structure
|
||||||
|
@BookID int,
|
||||||
|
@UserID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.Books b
|
||||||
|
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||||
|
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
|
||||||
|
WHERE b.BookID = @BookID
|
||||||
|
AND b.IsArchived = 0
|
||||||
|
AND p.IsArchived = 0
|
||||||
|
AND pua.UserID = @UserID
|
||||||
|
AND pua.IsActive = 1
|
||||||
|
)
|
||||||
|
RETURN;
|
||||||
|
|
||||||
|
SELECT @BookID AS BookId;
|
||||||
|
|
||||||
|
SELECT c.ChapterID AS ChapterId, c.ChapterTitle AS Title, c.SortOrder
|
||||||
|
FROM dbo.Chapters c
|
||||||
|
WHERE c.BookID = @BookID
|
||||||
|
AND c.IsArchived = 0
|
||||||
|
ORDER BY c.SortOrder, c.ChapterNumber, c.ChapterTitle, c.ChapterID;
|
||||||
|
|
||||||
|
SELECT s.ChapterID AS ChapterId, s.SceneID AS SceneId, s.SceneTitle AS Title, s.SortOrder,
|
||||||
|
rs.StatusName AS RevisionStatus,
|
||||||
|
sw.EstimatedWordCount AS EstimatedWords,
|
||||||
|
sw.ActualWordCount AS ActualWords
|
||||||
|
FROM dbo.Scenes s
|
||||||
|
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||||
|
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
|
||||||
|
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||||
|
WHERE c.BookID = @BookID
|
||||||
|
AND c.IsArchived = 0
|
||||||
|
AND s.IsArchived = 0
|
||||||
|
ORDER BY c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneTitle, s.SceneID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.WordCompanion_Chapter_List
|
||||||
|
@BookID int,
|
||||||
|
@UserID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
IF NOT EXISTS
|
||||||
|
(
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.Books b
|
||||||
|
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||||
|
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
|
||||||
|
WHERE b.BookID = @BookID
|
||||||
|
AND b.IsArchived = 0
|
||||||
|
AND p.IsArchived = 0
|
||||||
|
AND pua.UserID = @UserID
|
||||||
|
AND pua.IsActive = 1
|
||||||
|
)
|
||||||
|
RETURN;
|
||||||
|
|
||||||
|
SELECT c.ChapterID AS ChapterId,
|
||||||
|
c.ChapterTitle AS Title,
|
||||||
|
c.SortOrder
|
||||||
|
FROM dbo.Chapters c
|
||||||
|
WHERE c.BookID = @BookID
|
||||||
|
AND c.IsArchived = 0
|
||||||
|
ORDER BY c.SortOrder, c.ChapterNumber, c.ChapterTitle, c.ChapterID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
21
PlotLine/Sql/110_LocationLeafNameListOrdering.sql
Normal file
21
PlotLine/Sql/110_LocationLeafNameListOrdering.sql
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
SET ANSI_NULLS ON;
|
||||||
|
GO
|
||||||
|
SET QUOTED_IDENTIFIER ON;
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.Location_ListByProject
|
||||||
|
@ProjectID int
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT lp.LocationID, lp.ProjectID, lp.ParentLocationID, lp.LocationName, lp.LocationTypeID, lt.TypeName AS LocationTypeName,
|
||||||
|
lp.Description, lp.ShowInQuickAddBar, lp.ExcludeFromCompanionDetection, lp.DetectionPriority,
|
||||||
|
lp.LocationPath, lp.Depth, l.CreatedDate, l.UpdatedDate, l.IsArchived
|
||||||
|
FROM dbo.LocationPaths lp
|
||||||
|
INNER JOIN dbo.Locations l ON l.LocationID = lp.LocationID
|
||||||
|
LEFT JOIN dbo.LocationTypes lt ON lt.LocationTypeID = lp.LocationTypeID
|
||||||
|
WHERE lp.ProjectID = @ProjectID
|
||||||
|
ORDER BY lp.LocationName, lp.LocationPath, lp.LocationID;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
@ -3,7 +3,7 @@
|
|||||||
ViewData["ShellClass"] = "marketing-shell";
|
ViewData["ShellClass"] = "marketing-shell";
|
||||||
}
|
}
|
||||||
|
|
||||||
<article class="marketing-home">
|
<article class="marketing-home marketing-home-page">
|
||||||
<section class="marketing-hero reveal-section">
|
<section class="marketing-hero reveal-section">
|
||||||
<div class="marketing-container marketing-hero__grid">
|
<div class="marketing-container marketing-hero__grid">
|
||||||
<div class="marketing-hero__copy">
|
<div class="marketing-hero__copy">
|
||||||
|
|||||||
@ -71,10 +71,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<section class="mb-3" aria-label="Series summary">
|
<section class="mb-3" aria-label="Series summary">
|
||||||
<div class="row g-2">
|
<div class="dashboard-summary-grid">
|
||||||
@foreach (var card in summaryCards)
|
@foreach (var card in summaryCards)
|
||||||
{
|
{
|
||||||
<div class="col-6 col-md-4 col-xl-2">
|
<div>
|
||||||
<div class="card h-100 dashboard-summary-card @card.Item3">
|
<div class="card h-100 dashboard-summary-card @card.Item3">
|
||||||
<div class="card-body dashboard-summary-body">
|
<div class="card-body dashboard-summary-body">
|
||||||
<p class="eyebrow dashboard-summary-label">@card.Item1</p>
|
<p class="eyebrow dashboard-summary-label">@card.Item1</p>
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
ViewData["ShellClass"] = "marketing-shell";
|
ViewData["ShellClass"] = "marketing-shell";
|
||||||
}
|
}
|
||||||
|
|
||||||
<article class="marketing-home public-info-page">
|
<article class="marketing-home public-info-page public-about-page">
|
||||||
<section class="public-hero">
|
<section class="public-hero">
|
||||||
<div class="marketing-container public-hero__inner">
|
<div class="marketing-container public-hero__inner">
|
||||||
<p class="marketing-eyebrow">About PlotDirector</p>
|
<p class="marketing-eyebrow">About PlotDirector</p>
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
ViewData["ShellClass"] = "marketing-shell";
|
ViewData["ShellClass"] = "marketing-shell";
|
||||||
}
|
}
|
||||||
|
|
||||||
<article class="marketing-home public-info-page">
|
<article class="marketing-home public-info-page public-contact-page">
|
||||||
<section class="public-hero">
|
<section class="public-hero">
|
||||||
<div class="marketing-container public-hero__inner">
|
<div class="marketing-container public-hero__inner">
|
||||||
<p class="marketing-eyebrow">Contact</p>
|
<p class="marketing-eyebrow">Contact</p>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
2
PlotLine/wwwroot/css/plotline-theme.min.css
vendored
2
PlotLine/wwwroot/css/plotline-theme.min.css
vendored
File diff suppressed because one or more lines are too long
@ -470,20 +470,27 @@ td.text-end {
|
|||||||
border-color: var(--plotline-line);
|
border-color: var(--plotline-line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-summary-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(9, minmax(0, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-summary-body {
|
.dashboard-summary-body {
|
||||||
padding: 10px 12px;
|
padding: 8px 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-summary-label {
|
.dashboard-summary-label {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 3px;
|
||||||
font-size: 0.7rem;
|
font-size: 0.64rem;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-summary-value {
|
.dashboard-summary-value {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 1.45rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-summary-calm {
|
.dashboard-summary-calm {
|
||||||
@ -501,6 +508,24 @@ td.text-end {
|
|||||||
background: rgba(252, 241, 246, 0.7);
|
background: rgba(252, 241, 246, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1199.98px) {
|
||||||
|
.dashboard-summary-grid {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.dashboard-summary-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.dashboard-summary-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-progress-card {
|
.dashboard-progress-card {
|
||||||
border-color: rgba(47, 111, 99, 0.18);
|
border-color: rgba(47, 111, 99, 0.18);
|
||||||
background: linear-gradient(180deg, #ffffff 0%, rgba(238, 245, 242, 0.64) 100%);
|
background: linear-gradient(180deg, #ffffff 0%, rgba(238, 245, 242, 0.64) 100%);
|
||||||
|
|||||||
2
PlotLine/wwwroot/css/site.min.css
vendored
2
PlotLine/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1132,6 +1132,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const bookDisplayTitle = (book) => {
|
const bookDisplayTitle = (book) => {
|
||||||
|
const displayTitle = String(book?.displayTitle || "").trim();
|
||||||
|
if (displayTitle) {
|
||||||
|
return displayTitle;
|
||||||
|
}
|
||||||
|
|
||||||
const title = String(book?.title || "").trim();
|
const title = String(book?.title || "").trim();
|
||||||
const subtitle = String(book?.subtitle || "").trim();
|
const subtitle = String(book?.subtitle || "").trim();
|
||||||
return subtitle ? `${title}: ${subtitle}` : title;
|
return subtitle ? `${title}: ${subtitle}` : title;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user