SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'DisplayName') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD DisplayName nvarchar(200) NULL; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'Description') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD Description nvarchar(500) NULL; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'Prompt') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD Prompt nvarchar(max) NULL; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'GenerationAttemptCount') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD GenerationAttemptCount int NOT NULL CONSTRAINT DF_IllustrationLibraryItems_GenerationAttemptCount DEFAULT 0; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'ErrorMessage') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD ErrorMessage nvarchar(1000) NULL; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'RejectedUtc') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD RejectedUtc datetime2(0) NULL; GO IF COL_LENGTH(N'dbo.IllustrationLibraryItems', N'RejectedByUserID') IS NULL ALTER TABLE dbo.IllustrationLibraryItems ADD RejectedByUserID int NULL; GO UPDATE dbo.IllustrationLibraryItems SET DisplayName = ISNULL(DisplayName, DisplayTitle), Description = ISNULL(Description, ShortDescription), Prompt = ISNULL(Prompt, PromptText), GenerationAttemptCount = CASE WHEN GenerationAttemptCount = 0 THEN AttemptCount ELSE GenerationAttemptCount END, ErrorMessage = ISNULL(ErrorMessage, LastError) WHERE DisplayName IS NULL OR Description IS NULL OR Prompt IS NULL OR ErrorMessage IS NULL OR GenerationAttemptCount = 0; GO IF EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_IllustrationLibraryItems_Status') ALTER TABLE dbo.IllustrationLibraryItems DROP CONSTRAINT CK_IllustrationLibraryItems_Status; GO ALTER TABLE dbo.IllustrationLibraryItems ADD CONSTRAINT CK_IllustrationLibraryItems_Status CHECK (Status IN (N'Planned', N'Queued', N'Generating', N'Generated', N'Approved', N'Rejected', N'Failed', N'Superseded', N'Disabled')); GO IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_IllustrationLibraryItems_RejectedBy') AND OBJECT_ID(N'dbo.AppUser', N'U') IS NOT NULL BEGIN ALTER TABLE dbo.IllustrationLibraryItems ADD CONSTRAINT FK_IllustrationLibraryItems_RejectedBy FOREIGN KEY (RejectedByUserID) REFERENCES dbo.AppUser(UserID); END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_List @Category nvarchar(40) = NULL, @Status nvarchar(40) = NULL, @Search nvarchar(120) = NULL AS BEGIN SET NOCOUNT ON; SELECT * FROM dbo.IllustrationLibraryItems WHERE (@Category IS NULL OR Category = @Category) AND (@Status IS NULL OR Status = @Status) AND ( @Search IS NULL OR StableCode LIKE N'%' + @Search + N'%' OR ISNULL(DisplayName, DisplayTitle) LIKE N'%' + @Search + N'%' OR ISNULL(Description, ShortDescription) LIKE N'%' + @Search + N'%' ) ORDER BY CASE Status WHEN N'Generating' THEN 1 WHEN N'Queued' THEN 2 WHEN N'Generated' THEN 3 WHEN N'Planned' THEN 4 WHEN N'Failed' THEN 5 WHEN N'Rejected' THEN 6 WHEN N'Approved' THEN 7 WHEN N'Superseded' THEN 8 ELSE 9 END, UpdatedUtc DESC, IllustrationLibraryItemID DESC; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_Summary AS BEGIN SET NOCOUNT ON; SELECT COUNT(1) AS TotalCount, ISNULL(SUM(CASE WHEN Status = N'Planned' THEN 1 ELSE 0 END), 0) AS PlannedCount, ISNULL(SUM(CASE WHEN Status = N'Queued' THEN 1 ELSE 0 END), 0) AS QueuedCount, ISNULL(SUM(CASE WHEN Status = N'Generating' THEN 1 ELSE 0 END), 0) AS GeneratingCount, ISNULL(SUM(CASE WHEN Status = N'Generated' THEN 1 ELSE 0 END), 0) AS GeneratedCount, ISNULL(SUM(CASE WHEN Status = N'Approved' THEN 1 ELSE 0 END), 0) AS ApprovedCount, ISNULL(SUM(CASE WHEN Status = N'Rejected' THEN 1 ELSE 0 END), 0) AS RejectedCount, ISNULL(SUM(CASE WHEN Status = N'Failed' THEN 1 ELSE 0 END), 0) AS FailedCount, ISNULL(SUM(CASE WHEN Status = N'Superseded' THEN 1 ELSE 0 END), 0) AS SupersededCount, ISNULL(SUM(CASE WHEN Status = N'Disabled' THEN 1 ELSE 0 END), 0) AS DisabledCount FROM dbo.IllustrationLibraryItems; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_UpsertPlanned @Category nvarchar(40), @StableCode nvarchar(120), @DisplayTitle nvarchar(200), @ShortDescription nvarchar(500) = NULL, @MetadataJson nvarchar(max) = NULL, @SpecificationJson nvarchar(max) = NULL AS BEGIN SET NOCOUNT ON; IF EXISTS ( SELECT 1 FROM dbo.IllustrationLibraryItems WHERE StableCode = @StableCode AND ParentItemID IS NULL AND IsActive = 1 AND Status IN (N'Planned', N'Queued', N'Generating', N'Generated', N'Approved') ) BEGIN SELECT * FROM dbo.IllustrationLibraryItems WHERE StableCode = @StableCode AND ParentItemID IS NULL AND IsActive = 1 AND Status IN (N'Planned', N'Queued', N'Generating', N'Generated', N'Approved'); RETURN; END; INSERT dbo.IllustrationLibraryItems (Category, StableCode, DisplayTitle, DisplayName, ShortDescription, Description, MetadataJson, SpecificationJson, Status) VALUES (@Category, @StableCode, @DisplayTitle, @DisplayTitle, @ShortDescription, @ShortDescription, @MetadataJson, @SpecificationJson, N'Planned'); SELECT * FROM dbo.IllustrationLibraryItems WHERE IllustrationLibraryItemID = SCOPE_IDENTITY(); END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_ClaimNext AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRANSACTION; DECLARE @Claimed TABLE (IllustrationLibraryItemID int NOT NULL); ;WITH nextItem AS ( SELECT TOP (1) * FROM dbo.IllustrationLibraryItems WITH (UPDLOCK, READPAST, ROWLOCK) WHERE Status = N'Queued' AND IsActive = 1 AND GenerationAttemptCount < 3 ORDER BY QueuedUtc, IllustrationLibraryItemID ) UPDATE nextItem SET Status = N'Generating', AttemptCount = AttemptCount + 1, GenerationAttemptCount = GenerationAttemptCount + 1, StartedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME() OUTPUT inserted.IllustrationLibraryItemID INTO @Claimed; SELECT item.* FROM dbo.IllustrationLibraryItems item INNER JOIN @Claimed claimed ON claimed.IllustrationLibraryItemID = item.IllustrationLibraryItemID; COMMIT TRANSACTION; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_MarkGenerated @IllustrationLibraryItemID int, @FilePath nvarchar(500), @ThumbnailPath nvarchar(500), @Width int, @Height int, @FileSizeBytes bigint, @MimeType nvarchar(100), @Provider nvarchar(80), @Model nvarchar(120), @PromptText nvarchar(max), @PromptTemplateVersion nvarchar(40), @ProviderRequestID nvarchar(160) = NULL AS BEGIN SET NOCOUNT ON; UPDATE dbo.IllustrationLibraryItems SET Status = N'Generated', FilePath = @FilePath, ThumbnailPath = @ThumbnailPath, Width = @Width, Height = @Height, FileSizeBytes = @FileSizeBytes, MimeType = @MimeType, Provider = @Provider, Model = @Model, PromptText = @PromptText, Prompt = @PromptText, PromptTemplateVersion = @PromptTemplateVersion, ProviderRequestID = @ProviderRequestID, CompletedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME(), LastError = NULL, ErrorMessage = NULL WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID AND Status = N'Generating'; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_MarkFailed @IllustrationLibraryItemID int, @LastError nvarchar(1000) AS BEGIN SET NOCOUNT ON; UPDATE dbo.IllustrationLibraryItems SET Status = N'Failed', LastError = @LastError, ErrorMessage = @LastError, CompletedUtc = SYSUTCDATETIME(), UpdatedUtc = SYSUTCDATETIME() WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID AND Status = N'Generating'; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_Approve @IllustrationLibraryItemID int, @ApprovedByUserID int AS BEGIN SET NOCOUNT ON; DECLARE @StableCode nvarchar(120); SELECT @StableCode = StableCode FROM dbo.IllustrationLibraryItems WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID; UPDATE dbo.IllustrationLibraryItems SET IsApproved = 0, Status = N'Superseded', UpdatedUtc = SYSUTCDATETIME() WHERE StableCode = @StableCode AND IllustrationLibraryItemID <> @IllustrationLibraryItemID AND Status = N'Approved'; UPDATE dbo.IllustrationLibraryItems SET Status = N'Approved', IsApproved = 1, ApprovedUtc = SYSUTCDATETIME(), ApprovedByUserID = @ApprovedByUserID, UpdatedUtc = SYSUTCDATETIME() WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID AND Status = N'Generated' AND FilePath IS NOT NULL; END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_Reject @IllustrationLibraryItemID int, @RejectedByUserID int, @RejectionReason nvarchar(500) = NULL AS BEGIN SET NOCOUNT ON; UPDATE dbo.IllustrationLibraryItems SET Status = N'Rejected', IsApproved = 0, RejectedUtc = SYSUTCDATETIME(), RejectedByUserID = @RejectedByUserID, RejectionReason = @RejectionReason, UpdatedUtc = SYSUTCDATETIME() WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID AND Status IN (N'Generated', N'Approved'); END; GO CREATE OR ALTER PROCEDURE dbo.IllustrationLibraryItem_CreateRegeneration @IllustrationLibraryItemID int, @AdminNotes nvarchar(1000) = NULL AS BEGIN SET NOCOUNT ON; INSERT dbo.IllustrationLibraryItems (Category, StableCode, DisplayTitle, DisplayName, ShortDescription, Description, MetadataJson, SpecificationJson, Status, ParentItemID, AdminNotes) SELECT Category, StableCode, DisplayTitle, ISNULL(DisplayName, DisplayTitle), ShortDescription, ISNULL(Description, ShortDescription), MetadataJson, SpecificationJson, N'Planned', IllustrationLibraryItemID, @AdminNotes FROM dbo.IllustrationLibraryItems WHERE IllustrationLibraryItemID = @IllustrationLibraryItemID AND Status IN (N'Generated', N'Approved', N'Rejected', N'Failed', N'Superseded'); SELECT * FROM dbo.IllustrationLibraryItems WHERE IllustrationLibraryItemID = SCOPE_IDENTITY(); END; GO