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 string Title { get; set; } = string.Empty;
|
||||
public string? Subtitle { get; set; }
|
||||
public string DisplayTitle => BookTitleFormatter.DisplayTitle(Title, Subtitle);
|
||||
public int SortOrder { 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";
|
||||
}
|
||||
|
||||
<article class="marketing-home">
|
||||
<article class="marketing-home marketing-home-page">
|
||||
<section class="marketing-hero reveal-section">
|
||||
<div class="marketing-container marketing-hero__grid">
|
||||
<div class="marketing-hero__copy">
|
||||
|
||||
@ -71,10 +71,10 @@
|
||||
}
|
||||
|
||||
<section class="mb-3" aria-label="Series summary">
|
||||
<div class="row g-2">
|
||||
<div class="dashboard-summary-grid">
|
||||
@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-body dashboard-summary-body">
|
||||
<p class="eyebrow dashboard-summary-label">@card.Item1</p>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
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">
|
||||
<div class="marketing-container public-hero__inner">
|
||||
<p class="marketing-eyebrow">About PlotDirector</p>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
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">
|
||||
<div class="marketing-container public-hero__inner">
|
||||
<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);
|
||||
}
|
||||
|
||||
.dashboard-summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(9, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dashboard-summary-body {
|
||||
padding: 10px 12px;
|
||||
padding: 8px 9px;
|
||||
}
|
||||
|
||||
.dashboard-summary-label {
|
||||
margin-bottom: 4px;
|
||||
font-size: 0.7rem;
|
||||
margin-bottom: 3px;
|
||||
font-size: 0.64rem;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.dashboard-summary-value {
|
||||
display: block;
|
||||
font-size: 1.45rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.dashboard-summary-calm {
|
||||
@ -501,6 +508,24 @@ td.text-end {
|
||||
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 {
|
||||
border-color: rgba(47, 111, 99, 0.18);
|
||||
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 displayTitle = String(book?.displayTitle || "").trim();
|
||||
if (displayTitle) {
|
||||
return displayTitle;
|
||||
}
|
||||
|
||||
const title = String(book?.title || "").trim();
|
||||
const subtitle = String(book?.subtitle || "").trim();
|
||||
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