feat: just getting better and better
This commit is contained in:
@@ -317,7 +317,6 @@ public static class DevelopmentSeedExtensions
|
|||||||
{
|
{
|
||||||
Id = WorkspaceId,
|
Id = WorkspaceId,
|
||||||
Name = string.Empty,
|
Name = string.Empty,
|
||||||
Slug = string.Empty,
|
|
||||||
TimeZone = string.Empty,
|
TimeZone = string.Empty,
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
CreatedAt = DateTimeOffset.UtcNow,
|
||||||
};
|
};
|
||||||
@@ -325,7 +324,6 @@ public static class DevelopmentSeedExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
workspace.Name = "Northstar Studio";
|
workspace.Name = "Northstar Studio";
|
||||||
workspace.Slug = "northstar-studio";
|
|
||||||
workspace.OrganizationId = OrganizationId;
|
workspace.OrganizationId = OrganizationId;
|
||||||
workspace.OwnerUserId = managerUserId;
|
workspace.OwnerUserId = managerUserId;
|
||||||
workspace.TimeZone = "America/Montreal";
|
workspace.TimeZone = "America/Montreal";
|
||||||
|
|||||||
@@ -1,942 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
using Socialize.Api.Data;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(AppDbContext))]
|
|
||||||
[Migration("20260423061407_Initial")]
|
|
||||||
partial class Initial
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.0")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("ClaimType")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ClaimValue")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<Guid>("RoleId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.ToTable("AspNetRoleClaims", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("ClaimType")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ClaimValue")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<Guid>("UserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUserClaims", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("LoginProvider")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderKey")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ProviderDisplayName")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<Guid>("UserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("LoginProvider", "ProviderKey");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUserLogins", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("UserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("RoleId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("UserId", "RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUserRoles", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("UserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("LoginProvider")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("UserId", "LoginProvider", "Name");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUserTokens", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Approvals.Data.ApprovalDecision", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("ApprovalRequestId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Comment")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("DecidedByEmail")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("DecidedByName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("DecidedByUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Decision")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ApprovalRequestId");
|
|
||||||
|
|
||||||
b.ToTable("ApprovalDecisions", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Approvals.Data.ApprovalRequest", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("AccessToken")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("CompletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<Guid>("ContentItemId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("DueAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<Guid>("RequestedByUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("ReviewerEmail")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("ReviewerName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("SentAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("Stage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<string>("State")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId");
|
|
||||||
|
|
||||||
b.HasIndex("ReviewerEmail");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.ToTable("ApprovalRequests", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Assets.Data.Asset", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("AssetType")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ContentItemId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<int>("CurrentRevisionNumber")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("GoogleDriveFileId")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("GoogleDriveLink")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("PreviewUrl")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("SourceType")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.ToTable("Assets", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Assets.Data.AssetRevision", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("AssetId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatedByUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Notes")
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)");
|
|
||||||
|
|
||||||
b.Property<string>("PreviewUrl")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<int>("RevisionNumber")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("SourceReference")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("AssetId");
|
|
||||||
|
|
||||||
b.HasIndex("AssetId", "RevisionNumber")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("AssetRevisions", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Clients.Data.Client", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("PortraitUrl")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("PrimaryContactEmail")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("PrimaryContactName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("PrimaryContactPortraitUrl")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId", "Name")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Clients", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Comments.Data.Comment", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("AuthorDisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("AuthorEmail")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid>("AuthorUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Body")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ContentItemId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<bool>("IsResolved")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<Guid?>("ParentCommentId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("ResolvedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId");
|
|
||||||
|
|
||||||
b.HasIndex("ParentCommentId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.ToTable("Comments", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.ContentItems.Data.ContentItem", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("ClientId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("CurrentRevisionLabel")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<int>("CurrentRevisionNumber")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("DueDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Hashtags")
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ProjectId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("PublicationMessage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<string>("PublicationTargets")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("character varying(512)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ClientId");
|
|
||||||
|
|
||||||
b.HasIndex("ProjectId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.ToTable("ContentItems", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.ContentItems.Data.ContentItemRevision", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("ChangeSummary")
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)");
|
|
||||||
|
|
||||||
b.Property<Guid>("ContentItemId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<Guid?>("CreatedByUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Hashtags")
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)");
|
|
||||||
|
|
||||||
b.Property<string>("PublicationMessage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<string>("PublicationTargets")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(512)
|
|
||||||
.HasColumnType("character varying(512)");
|
|
||||||
|
|
||||||
b.Property<string>("RevisionLabel")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<int>("RevisionNumber")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId", "RevisionNumber")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("ContentItemRevisions", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Identity.Data.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("ConcurrencyStamp")
|
|
||||||
.IsConcurrencyToken()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("NormalizedName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NormalizedName")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("RoleNameIndex");
|
|
||||||
|
|
||||||
b.ToTable("AspNetRoles", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Identity.Data.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<int>("AccessFailedCount")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Address")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Alias")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("BirthDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("ConcurrencyStamp")
|
|
||||||
.IsConcurrencyToken()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<bool>("EmailConfirmed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("FacebookId")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Firstname")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("GoogleId")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Lastname")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<bool>("LockoutEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("NormalizedEmail")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("NormalizedUserName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("PhoneNumberConfirmed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("PortraitUrl")
|
|
||||||
.HasMaxLength(2048)
|
|
||||||
.HasColumnType("character varying(2048)");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.HasMaxLength(44)
|
|
||||||
.HasColumnType("character varying(44)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("RefreshTokenExpiryTime")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("SecurityStamp")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("TwoFactorEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NormalizedEmail")
|
|
||||||
.HasDatabaseName("EmailIndex");
|
|
||||||
|
|
||||||
b.HasIndex("NormalizedUserName")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("UserNameIndex");
|
|
||||||
|
|
||||||
b.ToTable("AspNetUsers", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Notifications.Data.NotificationEvent", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid?>("ContentItemId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<Guid>("EntityId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("EntityType")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.Property<string>("EventType")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.Property<string>("Message")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1024)
|
|
||||||
.HasColumnType("character varying(1024)");
|
|
||||||
|
|
||||||
b.Property<string>("MetadataJson")
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset?>("ReadAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("RecipientEmail")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid?>("RecipientUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ContentItemId");
|
|
||||||
|
|
||||||
b.HasIndex("CreatedAt");
|
|
||||||
|
|
||||||
b.HasIndex("RecipientUserId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.ToTable("NotificationEvents", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Projects.Data.Project", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<Guid>("ClientId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("EndDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Notes")
|
|
||||||
.HasMaxLength(4000)
|
|
||||||
.HasColumnType("character varying(4000)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("StartDate")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ClientId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.HasIndex("ClientId", "Name")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Projects", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Workspaces.Data.Workspace", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid>("OwnerUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Slug")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.Property<string>("TimeZone")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("OwnerUserId");
|
|
||||||
|
|
||||||
b.HasIndex("Slug")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Workspaces", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Socialize.Modules.Workspaces.Data.WorkspaceInvite", b =>
|
|
||||||
{
|
|
||||||
b.Property<Guid>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<Guid>("InvitedByUserId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.Property<string>("Role")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)");
|
|
||||||
|
|
||||||
b.Property<Guid>("WorkspaceId")
|
|
||||||
.HasColumnType("uuid");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId");
|
|
||||||
|
|
||||||
b.HasIndex("WorkspaceId", "Email", "Status");
|
|
||||||
|
|
||||||
b.ToTable("WorkspaceInvites", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.Role", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.User", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.User", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.Role", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.User", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Socialize.Modules.Identity.Data.User", null)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Socialize.Api.Data;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
[DbContext(typeof(AppDbContext))]
|
|
||||||
[Migration("20260430054500_AddWorkspaceLogo")]
|
|
||||||
public partial class AddWorkspaceLogo : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "LogoUrl",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "character varying(2048)",
|
|
||||||
maxLength: 2048,
|
|
||||||
nullable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "LogoUrl",
|
|
||||||
table: "Workspaces");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,116 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddFeedbackFoundation : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "FeedbackReports",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
|
||||||
Status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(8000)", maxLength: 8000, nullable: false),
|
|
||||||
ReporterUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
ReporterDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
ReporterEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
SubmittedPath = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
|
|
||||||
BrowserUserAgent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
|
||||||
ViewportWidth = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
ViewportHeight = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
AppVersion = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
|
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
WorkspaceName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
|
||||||
ClientId = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
ClientName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
|
||||||
ProjectId = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
ProjectName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
|
||||||
ContentItemId = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
ContentItemTitle = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
|
||||||
LastActivityAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
CancelledAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
|
||||||
CancelledByUserId = table.Column<Guid>(type: "uuid", nullable: true),
|
|
||||||
CancellationReason = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_FeedbackReports", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "FeedbackTags",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
NormalizedName = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_FeedbackTags", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_FeedbackTags_FeedbackReports_FeedbackReportId",
|
|
||||||
column: x => x.FeedbackReportId,
|
|
||||||
principalTable: "FeedbackReports",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackReports_LastActivityAt",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
column: "LastActivityAt");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackReports_ReporterUserId",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
column: "ReporterUserId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackReports_Status",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
column: "Status");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackReports_Type",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
column: "Type");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackReports_WorkspaceId",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
column: "WorkspaceId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackTags_FeedbackReportId_NormalizedName",
|
|
||||||
table: "FeedbackTags",
|
|
||||||
columns: new[] { "FeedbackReportId", "NormalizedName" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackTags_NormalizedName",
|
|
||||||
table: "FeedbackTags",
|
|
||||||
column: "NormalizedName");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "FeedbackTags");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "FeedbackReports");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddFeedbackScreenshots : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "FeedbackScreenshots",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
FileName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
ContentType = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
SizeBytes = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
BlobContainerName = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
BlobName = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_FeedbackScreenshots", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_FeedbackScreenshots_FeedbackReports_FeedbackReportId",
|
|
||||||
column: x => x.FeedbackReportId,
|
|
||||||
principalTable: "FeedbackReports",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackScreenshots_FeedbackReportId",
|
|
||||||
table: "FeedbackScreenshots",
|
|
||||||
column: "FeedbackReportId",
|
|
||||||
unique: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "FeedbackScreenshots");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddFeedbackCommentsActivity : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "FeedbackActivityEntries",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
ActorUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
ActorDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
ActorEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
ActivityType = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
FromValue = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true),
|
|
||||||
ToValue = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true),
|
|
||||||
Note = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_FeedbackActivityEntries", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_FeedbackActivityEntries_FeedbackReports_FeedbackReportId",
|
|
||||||
column: x => x.FeedbackReportId,
|
|
||||||
principalTable: "FeedbackReports",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "FeedbackComments",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
AuthorUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
AuthorDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
AuthorEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
AuthorRole = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
|
||||||
Body = table.Column<string>(type: "character varying(8000)", maxLength: 8000, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_FeedbackComments", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_FeedbackComments_FeedbackReports_FeedbackReportId",
|
|
||||||
column: x => x.FeedbackReportId,
|
|
||||||
principalTable: "FeedbackReports",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackActivityEntries_ActorUserId",
|
|
||||||
table: "FeedbackActivityEntries",
|
|
||||||
column: "ActorUserId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackActivityEntries_CreatedAt",
|
|
||||||
table: "FeedbackActivityEntries",
|
|
||||||
column: "CreatedAt");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackActivityEntries_FeedbackReportId",
|
|
||||||
table: "FeedbackActivityEntries",
|
|
||||||
column: "FeedbackReportId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackComments_AuthorUserId",
|
|
||||||
table: "FeedbackComments",
|
|
||||||
column: "AuthorUserId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackComments_CreatedAt",
|
|
||||||
table: "FeedbackComments",
|
|
||||||
column: "CreatedAt");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_FeedbackComments_FeedbackReportId",
|
|
||||||
table: "FeedbackComments",
|
|
||||||
column: "FeedbackReportId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "FeedbackActivityEntries");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "FeedbackComments");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddWorkspaceApprovalConfiguration : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "ApprovalMode",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "character varying(32)",
|
|
||||||
maxLength: 32,
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "Required");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "LockContentAfterApproval",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "SchedulePostsAutomaticallyOnApproval",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "SendAutomaticApprovalReminders",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "ApprovalMode",
|
|
||||||
table: "Workspaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "LockContentAfterApproval",
|
|
||||||
table: "Workspaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "SchedulePostsAutomaticallyOnApproval",
|
|
||||||
table: "Workspaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "SendAutomaticApprovalReminders",
|
|
||||||
table: "Workspaces");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddWorkspaceApprovalStepConfiguration : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "WorkspaceApprovalStepConfigurations",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
TargetType = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
|
||||||
TargetValue = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
RequiredApproverCount = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_WorkspaceApprovalStepConfigurations", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_WorkspaceApprovalStepConfigurations_WorkspaceId",
|
|
||||||
table: "WorkspaceApprovalStepConfigurations",
|
|
||||||
column: "WorkspaceId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_WorkspaceApprovalStepConfigurations_WorkspaceId_SortOrder",
|
|
||||||
table: "WorkspaceApprovalStepConfigurations",
|
|
||||||
columns: new[] { "WorkspaceId", "SortOrder" },
|
|
||||||
unique: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "WorkspaceApprovalStepConfigurations");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddApprovalWorkflowRuntime : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
|
||||||
name: "WorkflowInstanceId",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
type: "uuid",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
|
||||||
name: "WorkflowStepRequiredApproverCount",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
type: "integer",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
|
||||||
name: "WorkflowStepSortOrder",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
type: "integer",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "WorkflowStepTargetType",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
type: "character varying(32)",
|
|
||||||
maxLength: 32,
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "WorkflowStepTargetValue",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
type: "character varying(128)",
|
|
||||||
maxLength: 128,
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ApprovalWorkflowInstances",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
ContentItemId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
State = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
ApprovalMode = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
StartedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
|
||||||
CompletedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ApprovalWorkflowInstances", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ApprovalRequests_WorkflowInstanceId",
|
|
||||||
table: "ApprovalRequests",
|
|
||||||
column: "WorkflowInstanceId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ApprovalWorkflowInstances_ContentItemId",
|
|
||||||
table: "ApprovalWorkflowInstances",
|
|
||||||
column: "ContentItemId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ApprovalWorkflowInstances_ContentItemId_State",
|
|
||||||
table: "ApprovalWorkflowInstances",
|
|
||||||
columns: new[] { "ContentItemId", "State" },
|
|
||||||
unique: true,
|
|
||||||
filter: "\"State\" = 'Pending'");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ApprovalWorkflowInstances_WorkspaceId",
|
|
||||||
table: "ApprovalWorkflowInstances",
|
|
||||||
column: "WorkspaceId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ApprovalWorkflowInstances");
|
|
||||||
|
|
||||||
migrationBuilder.DropIndex(
|
|
||||||
name: "IX_ApprovalRequests_WorkflowInstanceId",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "WorkflowInstanceId",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "WorkflowStepRequiredApproverCount",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "WorkflowStepSortOrder",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "WorkflowStepTargetType",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "WorkflowStepTargetValue",
|
|
||||||
table: "ApprovalRequests");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class RenameProjectsToCampaigns : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.RenameTable(
|
|
||||||
name: "Projects",
|
|
||||||
newName: "Campaigns");
|
|
||||||
|
|
||||||
migrationBuilder.DropPrimaryKey(
|
|
||||||
name: "PK_Projects",
|
|
||||||
table: "Campaigns");
|
|
||||||
|
|
||||||
migrationBuilder.AddPrimaryKey(
|
|
||||||
name: "PK_Campaigns",
|
|
||||||
table: "Campaigns",
|
|
||||||
column: "Id");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Projects_WorkspaceId",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Campaigns_WorkspaceId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Projects_ClientId_Name",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Campaigns_ClientId_Name");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Projects_ClientId",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Campaigns_ClientId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "ProjectName",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
newName: "CampaignName");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "ProjectId",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
newName: "CampaignId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "ProjectId",
|
|
||||||
table: "ContentItems",
|
|
||||||
newName: "CampaignId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_ContentItems_ProjectId",
|
|
||||||
table: "ContentItems",
|
|
||||||
newName: "IX_ContentItems_CampaignId");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropPrimaryKey(
|
|
||||||
name: "PK_Campaigns",
|
|
||||||
table: "Campaigns");
|
|
||||||
|
|
||||||
migrationBuilder.AddPrimaryKey(
|
|
||||||
name: "PK_Projects",
|
|
||||||
table: "Campaigns",
|
|
||||||
column: "Id");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Campaigns_WorkspaceId",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Projects_WorkspaceId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Campaigns_ClientId_Name",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Projects_ClientId_Name");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_Campaigns_ClientId",
|
|
||||||
table: "Campaigns",
|
|
||||||
newName: "IX_Projects_ClientId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameTable(
|
|
||||||
name: "Campaigns",
|
|
||||||
newName: "Projects");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "CampaignName",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
newName: "ProjectName");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "CampaignId",
|
|
||||||
table: "FeedbackReports",
|
|
||||||
newName: "ProjectId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "CampaignId",
|
|
||||||
table: "ContentItems",
|
|
||||||
newName: "ProjectId");
|
|
||||||
|
|
||||||
migrationBuilder.RenameIndex(
|
|
||||||
name: "IX_ContentItems_CampaignId",
|
|
||||||
table: "ContentItems",
|
|
||||||
newName: "IX_ContentItems_ProjectId");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Socialize.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddOrganizations : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
|
||||||
name: "OrganizationId",
|
|
||||||
table: "Workspaces",
|
|
||||||
type: "uuid",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Organizations",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
OwnerUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Organizations", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "OrganizationMemberships",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
OrganizationId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Role = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_OrganizationMemberships", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_OrganizationMemberships_Organizations_OrganizationId",
|
|
||||||
column: x => x.OrganizationId,
|
|
||||||
principalTable: "Organizations",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.Sql(
|
|
||||||
"""
|
|
||||||
INSERT INTO "Organizations" ("Id", "Name", "OwnerUserId", "CreatedAt")
|
|
||||||
VALUES ('99999999-9999-9999-9999-999999999999', 'Northstar Collective', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', CURRENT_TIMESTAMP);
|
|
||||||
|
|
||||||
UPDATE "Workspaces"
|
|
||||||
SET "OrganizationId" = '99999999-9999-9999-9999-999999999999'
|
|
||||||
WHERE "OrganizationId" = '00000000-0000-0000-0000-000000000000';
|
|
||||||
|
|
||||||
INSERT INTO "OrganizationMemberships" ("Id", "OrganizationId", "UserId", "Role", "CreatedAt")
|
|
||||||
VALUES ('99999999-9999-9999-9999-000000000001', '99999999-9999-9999-9999-999999999999', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Owner', CURRENT_TIMESTAMP);
|
|
||||||
""");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Workspaces_OrganizationId",
|
|
||||||
table: "Workspaces",
|
|
||||||
column: "OrganizationId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OrganizationMemberships_OrganizationId",
|
|
||||||
table: "OrganizationMemberships",
|
|
||||||
column: "OrganizationId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OrganizationMemberships_OrganizationId_UserId",
|
|
||||||
table: "OrganizationMemberships",
|
|
||||||
columns: new[] { "OrganizationId", "UserId" },
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OrganizationMemberships_UserId",
|
|
||||||
table: "OrganizationMemberships",
|
|
||||||
column: "UserId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Organizations_OwnerUserId",
|
|
||||||
table: "Organizations",
|
|
||||||
column: "OwnerUserId");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "FK_Workspaces_Organizations_OrganizationId",
|
|
||||||
table: "Workspaces",
|
|
||||||
column: "OrganizationId",
|
|
||||||
principalTable: "Organizations",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Restrict);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "FK_Workspaces_Organizations_OrganizationId",
|
|
||||||
table: "Workspaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "OrganizationMemberships");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Organizations");
|
|
||||||
|
|
||||||
migrationBuilder.DropIndex(
|
|
||||||
name: "IX_Workspaces_OrganizationId",
|
|
||||||
table: "Workspaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "OrganizationId",
|
|
||||||
table: "Workspaces");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,8 +12,8 @@ using Socialize.Api.Data;
|
|||||||
namespace Socialize.Api.Migrations
|
namespace Socialize.Api.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(AppDbContext))]
|
[DbContext(typeof(AppDbContext))]
|
||||||
[Migration("20260504195518_AddOrganizations")]
|
[Migration("20260505013232_Initial")]
|
||||||
partial class AddOrganizations
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@@ -1314,11 +1314,6 @@ namespace Socialize.Api.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasDefaultValue(false);
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.Property<string>("Slug")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.Property<string>("TimeZone")
|
b.Property<string>("TimeZone")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
@@ -1330,9 +1325,6 @@ namespace Socialize.Api.Migrations
|
|||||||
|
|
||||||
b.HasIndex("OwnerUserId");
|
b.HasIndex("OwnerUserId");
|
||||||
|
|
||||||
b.HasIndex("Slug")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Workspaces", (string)null);
|
b.ToTable("Workspaces", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,6 +37,11 @@ namespace Socialize.Api.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ContentItemId = table.Column<Guid>(type: "uuid", nullable: false),
|
ContentItemId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
WorkflowInstanceId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
WorkflowStepSortOrder = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
WorkflowStepTargetType = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
|
||||||
|
WorkflowStepTargetValue = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
|
||||||
|
WorkflowStepRequiredApproverCount = table.Column<int>(type: "integer", nullable: true),
|
||||||
Stage = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
Stage = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
ReviewerName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
ReviewerName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
ReviewerEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
ReviewerEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
@@ -52,6 +57,23 @@ namespace Socialize.Api.Migrations
|
|||||||
table.PrimaryKey("PK_ApprovalRequests", x => x.Id);
|
table.PrimaryKey("PK_ApprovalRequests", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApprovalWorkflowInstances",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ContentItemId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
State = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
ApprovalMode = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
StartedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
||||||
|
CompletedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApprovalWorkflowInstances", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "AspNetRoles",
|
name: "AspNetRoles",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@@ -140,6 +162,26 @@ namespace Socialize.Api.Migrations
|
|||||||
table.PrimaryKey("PK_Assets", x => x.Id);
|
table.PrimaryKey("PK_Assets", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Campaigns",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ClientId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
||||||
|
Notes = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
||||||
|
Status = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
StartDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
EndDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Campaigns", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Clients",
|
name: "Clients",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@@ -208,7 +250,7 @@ namespace Socialize.Api.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ClientId = table.Column<Guid>(type: "uuid", nullable: false),
|
ClientId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ProjectId = table.Column<Guid>(type: "uuid", nullable: false),
|
CampaignId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Title = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Title = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
PublicationMessage = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: false),
|
PublicationMessage = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: false),
|
||||||
PublicationTargets = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
PublicationTargets = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
||||||
@@ -224,6 +266,41 @@ namespace Socialize.Api.Migrations
|
|||||||
table.PrimaryKey("PK_ContentItems", x => x.Id);
|
table.PrimaryKey("PK_ContentItems", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FeedbackReports",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Type = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
|
Status = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(8000)", maxLength: 8000, nullable: false),
|
||||||
|
ReporterUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ReporterDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
ReporterEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
SubmittedPath = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
|
||||||
|
BrowserUserAgent = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||||
|
ViewportWidth = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
ViewportHeight = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
AppVersion = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
|
||||||
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
WorkspaceName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
ClientId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ClientName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
CampaignId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
CampaignName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
ContentItemId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
ContentItemTitle = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
||||||
|
LastActivityAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
CancelledAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
CancelledByUserId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
CancellationReason = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FeedbackReports", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "NotificationEvents",
|
name: "NotificationEvents",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@@ -247,23 +324,35 @@ namespace Socialize.Api.Migrations
|
|||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Projects",
|
name: "Organizations",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
ClientId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
Description = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
OwnerUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Notes = table.Column<string>(type: "character varying(4000)", maxLength: 4000, nullable: true),
|
|
||||||
Status = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
|
||||||
StartDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
EndDate = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Projects", x => x.Id);
|
table.PrimaryKey("PK_Organizations", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "WorkspaceApprovalStepConfigurations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
WorkspaceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
TargetType = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
|
TargetValue = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
RequiredApproverCount = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_WorkspaceApprovalStepConfigurations", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
@@ -283,22 +372,6 @@ namespace Socialize.Api.Migrations
|
|||||||
table.PrimaryKey("PK_WorkspaceInvites", x => x.Id);
|
table.PrimaryKey("PK_WorkspaceInvites", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Workspaces",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
Slug = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
OwnerUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
|
||||||
TimeZone = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Workspaces", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "AspNetRoleClaims",
|
name: "AspNetRoleClaims",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@@ -405,6 +478,148 @@ namespace Socialize.Api.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FeedbackActivityEntries",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ActorUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
ActorDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
ActorEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
ActivityType = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
FromValue = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true),
|
||||||
|
ToValue = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: true),
|
||||||
|
Note = table.Column<string>(type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FeedbackActivityEntries", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FeedbackActivityEntries_FeedbackReports_FeedbackReportId",
|
||||||
|
column: x => x.FeedbackReportId,
|
||||||
|
principalTable: "FeedbackReports",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FeedbackComments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
AuthorUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
AuthorDisplayName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
AuthorEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
AuthorRole = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
|
Body = table.Column<string>(type: "character varying(8000)", maxLength: 8000, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FeedbackComments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FeedbackComments_FeedbackReports_FeedbackReportId",
|
||||||
|
column: x => x.FeedbackReportId,
|
||||||
|
principalTable: "FeedbackReports",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FeedbackScreenshots",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
FileName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
ContentType = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
SizeBytes = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
BlobContainerName = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
BlobName = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FeedbackScreenshots", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FeedbackScreenshots_FeedbackReports_FeedbackReportId",
|
||||||
|
column: x => x.FeedbackReportId,
|
||||||
|
principalTable: "FeedbackReports",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FeedbackTags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
FeedbackReportId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
NormalizedName = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FeedbackTags", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FeedbackTags_FeedbackReports_FeedbackReportId",
|
||||||
|
column: x => x.FeedbackReportId,
|
||||||
|
principalTable: "FeedbackReports",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OrganizationMemberships",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
OrganizationId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
UserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Role = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OrganizationMemberships", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_OrganizationMemberships_Organizations_OrganizationId",
|
||||||
|
column: x => x.OrganizationId,
|
||||||
|
principalTable: "Organizations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Workspaces",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
|
LogoUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||||
|
OrganizationId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
OwnerUserId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
TimeZone = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
ApprovalMode = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false, defaultValue: "Required"),
|
||||||
|
SchedulePostsAutomaticallyOnApproval = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||||
|
LockContentAfterApproval = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||||
|
SendAutomaticApprovalReminders = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Workspaces", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Workspaces_Organizations_OrganizationId",
|
||||||
|
column: x => x.OrganizationId,
|
||||||
|
principalTable: "Organizations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ApprovalDecisions_ApprovalRequestId",
|
name: "IX_ApprovalDecisions_ApprovalRequestId",
|
||||||
table: "ApprovalDecisions",
|
table: "ApprovalDecisions",
|
||||||
@@ -420,11 +635,33 @@ namespace Socialize.Api.Migrations
|
|||||||
table: "ApprovalRequests",
|
table: "ApprovalRequests",
|
||||||
column: "ReviewerEmail");
|
column: "ReviewerEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApprovalRequests_WorkflowInstanceId",
|
||||||
|
table: "ApprovalRequests",
|
||||||
|
column: "WorkflowInstanceId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ApprovalRequests_WorkspaceId",
|
name: "IX_ApprovalRequests_WorkspaceId",
|
||||||
table: "ApprovalRequests",
|
table: "ApprovalRequests",
|
||||||
column: "WorkspaceId");
|
column: "WorkspaceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApprovalWorkflowInstances_ContentItemId",
|
||||||
|
table: "ApprovalWorkflowInstances",
|
||||||
|
column: "ContentItemId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApprovalWorkflowInstances_ContentItemId_State",
|
||||||
|
table: "ApprovalWorkflowInstances",
|
||||||
|
columns: new[] { "ContentItemId", "State" },
|
||||||
|
unique: true,
|
||||||
|
filter: "\"State\" = 'Pending'");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApprovalWorkflowInstances_WorkspaceId",
|
||||||
|
table: "ApprovalWorkflowInstances",
|
||||||
|
column: "WorkspaceId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_AspNetRoleClaims_RoleId",
|
name: "IX_AspNetRoleClaims_RoleId",
|
||||||
table: "AspNetRoleClaims",
|
table: "AspNetRoleClaims",
|
||||||
@@ -483,6 +720,22 @@ namespace Socialize.Api.Migrations
|
|||||||
table: "Assets",
|
table: "Assets",
|
||||||
column: "WorkspaceId");
|
column: "WorkspaceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Campaigns_ClientId",
|
||||||
|
table: "Campaigns",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Campaigns_ClientId_Name",
|
||||||
|
table: "Campaigns",
|
||||||
|
columns: new[] { "ClientId", "Name" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Campaigns_WorkspaceId",
|
||||||
|
table: "Campaigns",
|
||||||
|
column: "WorkspaceId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Clients_WorkspaceId",
|
name: "IX_Clients_WorkspaceId",
|
||||||
table: "Clients",
|
table: "Clients",
|
||||||
@@ -520,21 +773,93 @@ namespace Socialize.Api.Migrations
|
|||||||
columns: new[] { "ContentItemId", "RevisionNumber" },
|
columns: new[] { "ContentItemId", "RevisionNumber" },
|
||||||
unique: true);
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ContentItems_CampaignId",
|
||||||
|
table: "ContentItems",
|
||||||
|
column: "CampaignId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ContentItems_ClientId",
|
name: "IX_ContentItems_ClientId",
|
||||||
table: "ContentItems",
|
table: "ContentItems",
|
||||||
column: "ClientId");
|
column: "ClientId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_ContentItems_ProjectId",
|
|
||||||
table: "ContentItems",
|
|
||||||
column: "ProjectId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ContentItems_WorkspaceId",
|
name: "IX_ContentItems_WorkspaceId",
|
||||||
table: "ContentItems",
|
table: "ContentItems",
|
||||||
column: "WorkspaceId");
|
column: "WorkspaceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackActivityEntries_ActorUserId",
|
||||||
|
table: "FeedbackActivityEntries",
|
||||||
|
column: "ActorUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackActivityEntries_CreatedAt",
|
||||||
|
table: "FeedbackActivityEntries",
|
||||||
|
column: "CreatedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackActivityEntries_FeedbackReportId",
|
||||||
|
table: "FeedbackActivityEntries",
|
||||||
|
column: "FeedbackReportId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackComments_AuthorUserId",
|
||||||
|
table: "FeedbackComments",
|
||||||
|
column: "AuthorUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackComments_CreatedAt",
|
||||||
|
table: "FeedbackComments",
|
||||||
|
column: "CreatedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackComments_FeedbackReportId",
|
||||||
|
table: "FeedbackComments",
|
||||||
|
column: "FeedbackReportId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackReports_LastActivityAt",
|
||||||
|
table: "FeedbackReports",
|
||||||
|
column: "LastActivityAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackReports_ReporterUserId",
|
||||||
|
table: "FeedbackReports",
|
||||||
|
column: "ReporterUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackReports_Status",
|
||||||
|
table: "FeedbackReports",
|
||||||
|
column: "Status");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackReports_Type",
|
||||||
|
table: "FeedbackReports",
|
||||||
|
column: "Type");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackReports_WorkspaceId",
|
||||||
|
table: "FeedbackReports",
|
||||||
|
column: "WorkspaceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackScreenshots_FeedbackReportId",
|
||||||
|
table: "FeedbackScreenshots",
|
||||||
|
column: "FeedbackReportId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackTags_FeedbackReportId_NormalizedName",
|
||||||
|
table: "FeedbackTags",
|
||||||
|
columns: new[] { "FeedbackReportId", "NormalizedName" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FeedbackTags_NormalizedName",
|
||||||
|
table: "FeedbackTags",
|
||||||
|
column: "NormalizedName");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_NotificationEvents_ContentItemId",
|
name: "IX_NotificationEvents_ContentItemId",
|
||||||
table: "NotificationEvents",
|
table: "NotificationEvents",
|
||||||
@@ -556,21 +881,37 @@ namespace Socialize.Api.Migrations
|
|||||||
column: "WorkspaceId");
|
column: "WorkspaceId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Projects_ClientId",
|
name: "IX_OrganizationMemberships_OrganizationId",
|
||||||
table: "Projects",
|
table: "OrganizationMemberships",
|
||||||
column: "ClientId");
|
column: "OrganizationId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Projects_ClientId_Name",
|
name: "IX_OrganizationMemberships_OrganizationId_UserId",
|
||||||
table: "Projects",
|
table: "OrganizationMemberships",
|
||||||
columns: new[] { "ClientId", "Name" },
|
columns: new[] { "OrganizationId", "UserId" },
|
||||||
unique: true);
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Projects_WorkspaceId",
|
name: "IX_OrganizationMemberships_UserId",
|
||||||
table: "Projects",
|
table: "OrganizationMemberships",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Organizations_OwnerUserId",
|
||||||
|
table: "Organizations",
|
||||||
|
column: "OwnerUserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_WorkspaceApprovalStepConfigurations_WorkspaceId",
|
||||||
|
table: "WorkspaceApprovalStepConfigurations",
|
||||||
column: "WorkspaceId");
|
column: "WorkspaceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_WorkspaceApprovalStepConfigurations_WorkspaceId_SortOrder",
|
||||||
|
table: "WorkspaceApprovalStepConfigurations",
|
||||||
|
columns: new[] { "WorkspaceId", "SortOrder" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_WorkspaceInvites_WorkspaceId",
|
name: "IX_WorkspaceInvites_WorkspaceId",
|
||||||
table: "WorkspaceInvites",
|
table: "WorkspaceInvites",
|
||||||
@@ -581,16 +922,15 @@ namespace Socialize.Api.Migrations
|
|||||||
table: "WorkspaceInvites",
|
table: "WorkspaceInvites",
|
||||||
columns: new[] { "WorkspaceId", "Email", "Status" });
|
columns: new[] { "WorkspaceId", "Email", "Status" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Workspaces_OrganizationId",
|
||||||
|
table: "Workspaces",
|
||||||
|
column: "OrganizationId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Workspaces_OwnerUserId",
|
name: "IX_Workspaces_OwnerUserId",
|
||||||
table: "Workspaces",
|
table: "Workspaces",
|
||||||
column: "OwnerUserId");
|
column: "OwnerUserId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Workspaces_Slug",
|
|
||||||
table: "Workspaces",
|
|
||||||
column: "Slug",
|
|
||||||
unique: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -602,6 +942,9 @@ namespace Socialize.Api.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "ApprovalRequests");
|
name: "ApprovalRequests");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApprovalWorkflowInstances");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "AspNetRoleClaims");
|
name: "AspNetRoleClaims");
|
||||||
|
|
||||||
@@ -623,6 +966,9 @@ namespace Socialize.Api.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Assets");
|
name: "Assets");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Campaigns");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Clients");
|
name: "Clients");
|
||||||
|
|
||||||
@@ -635,11 +981,26 @@ namespace Socialize.Api.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "ContentItems");
|
name: "ContentItems");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FeedbackActivityEntries");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FeedbackComments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FeedbackScreenshots");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FeedbackTags");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "NotificationEvents");
|
name: "NotificationEvents");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Projects");
|
name: "OrganizationMemberships");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "WorkspaceApprovalStepConfigurations");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "WorkspaceInvites");
|
name: "WorkspaceInvites");
|
||||||
@@ -652,6 +1013,12 @@ namespace Socialize.Api.Migrations
|
|||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "AspNetUsers");
|
name: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FeedbackReports");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Organizations");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1311,11 +1311,6 @@ namespace Socialize.Api.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasDefaultValue(false);
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.Property<string>("Slug")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)");
|
|
||||||
|
|
||||||
b.Property<string>("TimeZone")
|
b.Property<string>("TimeZone")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
@@ -1327,9 +1322,6 @@ namespace Socialize.Api.Migrations
|
|||||||
|
|
||||||
b.HasIndex("OwnerUserId");
|
b.HasIndex("OwnerUserId");
|
||||||
|
|
||||||
b.HasIndex("Slug")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Workspaces", (string)null);
|
b.ToTable("Workspaces", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ public class Workspace
|
|||||||
{
|
{
|
||||||
public Guid Id { get; init; }
|
public Guid Id { get; init; }
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required string Slug { get; set; }
|
|
||||||
public string? LogoUrl { get; set; }
|
public string? LogoUrl { get; set; }
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public Guid OwnerUserId { get; set; }
|
public Guid OwnerUserId { get; set; }
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public static class WorkspaceModelConfiguration
|
|||||||
workspace.ToTable("Workspaces");
|
workspace.ToTable("Workspaces");
|
||||||
workspace.HasKey(x => x.Id);
|
workspace.HasKey(x => x.Id);
|
||||||
workspace.Property(x => x.Name).HasMaxLength(256).IsRequired();
|
workspace.Property(x => x.Name).HasMaxLength(256).IsRequired();
|
||||||
workspace.Property(x => x.Slug).HasMaxLength(128).IsRequired();
|
|
||||||
workspace.Property(x => x.LogoUrl).HasMaxLength(2048);
|
workspace.Property(x => x.LogoUrl).HasMaxLength(2048);
|
||||||
workspace.Property(x => x.TimeZone).HasMaxLength(128).IsRequired();
|
workspace.Property(x => x.TimeZone).HasMaxLength(128).IsRequired();
|
||||||
workspace.Property(x => x.ApprovalMode).HasMaxLength(32).IsRequired().HasDefaultValue("Required");
|
workspace.Property(x => x.ApprovalMode).HasMaxLength(32).IsRequired().HasDefaultValue("Required");
|
||||||
@@ -22,7 +21,6 @@ public static class WorkspaceModelConfiguration
|
|||||||
workspace.Property(x => x.CreatedAt)
|
workspace.Property(x => x.CreatedAt)
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||||
workspace.HasIndex(x => x.Slug).IsUnique();
|
|
||||||
workspace.HasIndex(x => x.OrganizationId);
|
workspace.HasIndex(x => x.OrganizationId);
|
||||||
workspace.HasIndex(x => x.OwnerUserId);
|
workspace.HasIndex(x => x.OwnerUserId);
|
||||||
workspace.HasOne<Organization>()
|
workspace.HasOne<Organization>()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ namespace Socialize.Api.Modules.Workspaces.Handlers;
|
|||||||
public record CreateWorkspaceRequest(
|
public record CreateWorkspaceRequest(
|
||||||
Guid OrganizationId,
|
Guid OrganizationId,
|
||||||
string Name,
|
string Name,
|
||||||
string Slug,
|
|
||||||
string TimeZone);
|
string TimeZone);
|
||||||
|
|
||||||
public class CreateWorkspaceRequestValidator
|
public class CreateWorkspaceRequestValidator
|
||||||
@@ -19,10 +18,6 @@ public class CreateWorkspaceRequestValidator
|
|||||||
{
|
{
|
||||||
RuleFor(x => x.OrganizationId).NotEmpty();
|
RuleFor(x => x.OrganizationId).NotEmpty();
|
||||||
RuleFor(x => x.Name).NotEmpty().MaximumLength(256);
|
RuleFor(x => x.Name).NotEmpty().MaximumLength(256);
|
||||||
RuleFor(x => x.Slug)
|
|
||||||
.NotEmpty()
|
|
||||||
.MaximumLength(128)
|
|
||||||
.Matches("^[a-z0-9]+(?:-[a-z0-9]+)*$");
|
|
||||||
RuleFor(x => x.TimeZone).NotEmpty().MaximumLength(128);
|
RuleFor(x => x.TimeZone).NotEmpty().MaximumLength(128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,25 +51,13 @@ public class CreateWorkspaceHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
string normalizedName = request.Name.Trim();
|
string normalizedName = request.Name.Trim();
|
||||||
string normalizedSlug = request.Slug.Trim().ToLowerInvariant();
|
|
||||||
string normalizedTimeZone = request.TimeZone.Trim();
|
string normalizedTimeZone = request.TimeZone.Trim();
|
||||||
|
|
||||||
bool duplicateWorkspace = await dbContext.Workspaces
|
|
||||||
.AnyAsync(workspace => workspace.Slug == normalizedSlug, ct);
|
|
||||||
|
|
||||||
if (duplicateWorkspace)
|
|
||||||
{
|
|
||||||
AddError(request => request.Slug, "A workspace with this slug already exists.");
|
|
||||||
await SendErrorsAsync(StatusCodes.Status409Conflict, ct);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Workspace workspace = new()
|
Workspace workspace = new()
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
OrganizationId = request.OrganizationId,
|
OrganizationId = request.OrganizationId,
|
||||||
Name = normalizedName,
|
Name = normalizedName,
|
||||||
Slug = normalizedSlug,
|
|
||||||
OwnerUserId = User.GetUserId(),
|
OwnerUserId = User.GetUserId(),
|
||||||
TimeZone = normalizedTimeZone,
|
TimeZone = normalizedTimeZone,
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
CreatedAt = DateTimeOffset.UtcNow,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ public record WorkspaceDto(
|
|||||||
Guid Id,
|
Guid Id,
|
||||||
Guid OrganizationId,
|
Guid OrganizationId,
|
||||||
string Name,
|
string Name,
|
||||||
string Slug,
|
|
||||||
string? LogoUrl,
|
string? LogoUrl,
|
||||||
string TimeZone,
|
string TimeZone,
|
||||||
string ApprovalMode,
|
string ApprovalMode,
|
||||||
@@ -39,7 +38,6 @@ public record WorkspaceDto(
|
|||||||
workspace.Id,
|
workspace.Id,
|
||||||
workspace.OrganizationId,
|
workspace.OrganizationId,
|
||||||
workspace.Name,
|
workspace.Name,
|
||||||
workspace.Slug,
|
|
||||||
workspace.LogoUrl,
|
workspace.LogoUrl,
|
||||||
workspace.TimeZone,
|
workspace.TimeZone,
|
||||||
workspace.ApprovalMode,
|
workspace.ApprovalMode,
|
||||||
|
|||||||
27
docs/TASKS/content/001-merge-calendar-and-content-views.md
Normal file
27
docs/TASKS/content/001-merge-calendar-and-content-views.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Task: Merge content calendar and list navigation
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Expose calendar and content list workflows from a single Content sidebar entry.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Keep one sidebar item named Content using the calendar icon.
|
||||||
|
- Route the old workspace calendar entry to the Content page.
|
||||||
|
- Add Month, Week, and Upcoming views to the Content page.
|
||||||
|
- Remove in-view new content buttons because the app bar already exposes creation.
|
||||||
|
|
||||||
|
## Relevant Files
|
||||||
|
|
||||||
|
- `frontend/src/layouts/main/AppSidebar.vue`
|
||||||
|
- `frontend/src/router/router.js`
|
||||||
|
- `frontend/src/features/content/views/ContentItemsView.vue`
|
||||||
|
- `frontend/src/locales/en.json`
|
||||||
|
- `frontend/src/locales/fr.json`
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
46
docs/TASKS/organizations/005-remove-workspace-url-key.md
Normal file
46
docs/TASKS/organizations/005-remove-workspace-url-key.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Task: Remove workspace URL key
|
||||||
|
|
||||||
|
## Feature
|
||||||
|
|
||||||
|
`docs/FEATURES/organizations.md`
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Remove the human-managed workspace URL key from the product, API, database model, frontend UI, and generated contracts.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Workspaces are selected and routed by stable identifiers. The create workspace page still exposes a manual URL key field and the backend still persists a dedicated column for it.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Remove the URL key from workspace persistence and model configuration.
|
||||||
|
- Remove the URL key from workspace create requests and workspace DTO responses.
|
||||||
|
- Remove URL key validation and duplicate checks from workspace creation.
|
||||||
|
- Remove URL key UI, preview copy, and locale strings from the new-workspace page.
|
||||||
|
- Remove URL key display from organization workspace lists.
|
||||||
|
- Update generated OpenAPI/schema artifacts after the backend contract change.
|
||||||
|
- Rename any frontend-only helper that is not actually a product URL key.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Keep workspace ownership under organizations intact.
|
||||||
|
- Do not redesign workspace creation beyond removing the URL key UI.
|
||||||
|
- Preserve existing workspace/channel behavior except for deleting the workspace URL key.
|
||||||
|
|
||||||
|
## Done When
|
||||||
|
|
||||||
|
- [x] Workspace creation no longer accepts or sends the URL key.
|
||||||
|
- [x] Workspace responses no longer include the URL key.
|
||||||
|
- [x] The current EF model no longer contains the workspace URL key column or index.
|
||||||
|
- [x] The new-workspace page no longer references the URL key.
|
||||||
|
- [x] OpenAPI and frontend schema artifacts are regenerated.
|
||||||
|
|
||||||
|
## Validation Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet build backend/Socialize.slnx
|
||||||
|
dotnet test backend/Socialize.slnx
|
||||||
|
cd frontend
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-boundary-toggle {
|
.sidebar-boundary-toggle {
|
||||||
@apply absolute left-full top-8 z-40 flex h-10 w-10 -translate-x-1/2 items-center justify-center rounded-full border transition-colors;
|
@apply absolute left-full top-[1.48rem] z-40 flex h-[1.8rem] w-[1.8rem] -translate-x-1/2 items-center justify-center rounded-full border transition-colors;
|
||||||
background: rgba(255, 250, 242, 0.98);
|
background: rgba(255, 250, 242, 0.98);
|
||||||
border-color: rgba(23, 32, 51, 0.12);
|
border-color: rgba(23, 32, 51, 0.12);
|
||||||
color: #44516a;
|
color: #44516a;
|
||||||
@@ -97,6 +97,10 @@
|
|||||||
color: #fffaf2;
|
color: #fffaf2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-boundary-toggle :deep(.v-icon) {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.shell-view {
|
.shell-view {
|
||||||
@apply flex min-w-0 flex-1;
|
@apply flex min-w-0 flex-1;
|
||||||
}
|
}
|
||||||
|
|||||||
2
frontend/src/api/schema.d.ts
vendored
2
frontend/src/api/schema.d.ts
vendored
@@ -913,7 +913,6 @@ export interface components {
|
|||||||
/** Format: guid */
|
/** Format: guid */
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
slug?: string;
|
|
||||||
logoUrl?: string | null;
|
logoUrl?: string | null;
|
||||||
timeZone?: string;
|
timeZone?: string;
|
||||||
approvalMode?: string;
|
approvalMode?: string;
|
||||||
@@ -943,7 +942,6 @@ export interface components {
|
|||||||
/** Format: guid */
|
/** Format: guid */
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
|
||||||
timeZone: string;
|
timeZone: string;
|
||||||
};
|
};
|
||||||
SocializeApiModulesWorkspacesHandlersWorkspaceInviteDto: {
|
SocializeApiModulesWorkspacesHandlersWorkspaceInviteDto: {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createMemoryHistory, createRouter, RouterView } from 'vue-router';
|
|||||||
import { createHead, renderHeadToString } from '@vueuse/head';
|
import { createHead, renderHeadToString } from '@vueuse/head';
|
||||||
import { renderToString } from '@vue/server-renderer';
|
import { renderToString } from '@vue/server-renderer';
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
import en from '@/locales/en.json';
|
import en from '@/locales/en.json';
|
||||||
import fr from '@/locales/fr.json';
|
import fr from '@/locales/fr.json';
|
||||||
import Landing from '@/features/landing/views/Landing.vue';
|
import Landing from '@/features/landing/views/Landing.vue';
|
||||||
@@ -42,6 +43,7 @@ export async function render(routePath) {
|
|||||||
render: () => h(RouterView),
|
render: () => h(RouterView),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.use(createPinia());
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(head);
|
app.use(head);
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
|
|||||||
@@ -145,14 +145,20 @@
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
returnUrl: {
|
returnUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '/landing',
|
default: '/app/dashboard',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getPostLoginUrl() {
|
||||||
|
return props.returnUrl?.startsWith('/app')
|
||||||
|
? props.returnUrl
|
||||||
|
: '/app/dashboard';
|
||||||
|
}
|
||||||
|
|
||||||
async function handleLocalLogin() {
|
async function handleLocalLogin() {
|
||||||
try {
|
try {
|
||||||
await authStore.login(email.value, password.value);
|
await authStore.login(email.value, password.value);
|
||||||
await router.push(props.returnUrl);
|
await router.push(getPostLoginUrl());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login failed:', error);
|
console.error('Login failed:', error);
|
||||||
errorSnackBar.value = true;
|
errorSnackBar.value = true;
|
||||||
@@ -163,7 +169,7 @@
|
|||||||
try {
|
try {
|
||||||
const response = await authStore.loginWithGoogle(JSON.stringify(token));
|
const response = await authStore.loginWithGoogle(JSON.stringify(token));
|
||||||
if (response === true) {
|
if (response === true) {
|
||||||
await router.push(props.returnUrl);
|
await router.push(getPostLoginUrl());
|
||||||
} else {
|
} else {
|
||||||
errorSnackBar.value = true;
|
errorSnackBar.value = true;
|
||||||
}
|
}
|
||||||
@@ -177,7 +183,7 @@
|
|||||||
try {
|
try {
|
||||||
const response = await loginWithFacebook();
|
const response = await loginWithFacebook();
|
||||||
if (response === true) {
|
if (response === true) {
|
||||||
await router.push(props.returnUrl);
|
await router.push(getPostLoginUrl());
|
||||||
} else {
|
} else {
|
||||||
errorSnackBar.value = true;
|
errorSnackBar.value = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
import { computed, reactive, ref, watch } from 'vue';
|
import { computed, reactive, ref, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
|
||||||
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
|
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
|
||||||
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
|
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
|
||||||
import { useCampaignsStore } from '@/features/campaigns/stores/campaignsStore.js';
|
import { useCampaignsStore } from '@/features/campaigns/stores/campaignsStore.js';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const authStore = useAuthStore();
|
|
||||||
const workspaceStore = useWorkspaceStore();
|
const workspaceStore = useWorkspaceStore();
|
||||||
const clientsStore = useClientsStore();
|
const clientsStore = useClientsStore();
|
||||||
const campaignsStore = useCampaignsStore();
|
const campaignsStore = useCampaignsStore();
|
||||||
@@ -114,16 +112,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-row">
|
|
||||||
<button
|
|
||||||
v-if="authStore.isManager"
|
|
||||||
class="create-button"
|
|
||||||
@click="openCreateForm"
|
|
||||||
>
|
|
||||||
{{ t('campaigns.newCampaign') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isCreateFormVisible"
|
v-if="isCreateFormVisible"
|
||||||
class="create-panel"
|
class="create-panel"
|
||||||
@@ -274,17 +262,11 @@
|
|||||||
color: #526178;
|
color: #526178;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-row {
|
|
||||||
@apply flex justify-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-button,
|
|
||||||
.primary,
|
.primary,
|
||||||
.secondary {
|
.secondary {
|
||||||
@apply inline-flex items-center justify-center rounded-full px-5 py-3 text-sm font-bold transition;
|
@apply inline-flex items-center justify-center rounded-full px-5 py-3 text-sm font-bold transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-button,
|
|
||||||
.primary {
|
.primary {
|
||||||
background: #172033;
|
background: #172033;
|
||||||
color: #fffaf2;
|
color: #fffaf2;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const useChannelsStore = defineStore('channels', () => {
|
|||||||
|
|
||||||
for (const item of contentItemsStore.items) {
|
for (const item of contentItemsStore.items) {
|
||||||
for (const name of parseTargets(item.publicationTargets)) {
|
for (const name of parseTargets(item.publicationTargets)) {
|
||||||
const key = slugify(name);
|
const key = normalizeChannelKey(name);
|
||||||
const existing = derivedChannels.get(key) ?? {
|
const existing = derivedChannels.get(key) ?? {
|
||||||
id: key,
|
id: key,
|
||||||
name,
|
name,
|
||||||
@@ -95,7 +95,7 @@ export const useChannelsStore = defineStore('channels', () => {
|
|||||||
[currentWorkspaceId]: [
|
[currentWorkspaceId]: [
|
||||||
...next,
|
...next,
|
||||||
{
|
{
|
||||||
id: slugify(`${normalizedNetwork}-${normalizedName}`),
|
id: normalizeChannelKey(`${normalizedNetwork}-${normalizedName}`),
|
||||||
name: normalizedName,
|
name: normalizedName,
|
||||||
network: normalizedNetwork,
|
network: normalizedNetwork,
|
||||||
},
|
},
|
||||||
@@ -110,7 +110,7 @@ export const useChannelsStore = defineStore('channels', () => {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
function slugify(value) {
|
function normalizeChannelKey(value) {
|
||||||
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,280 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
||||||
|
import { useCampaignsStore } from '@/features/campaigns/stores/campaignsStore.js';
|
||||||
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
|
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
const authStore = useAuthStore();
|
const campaignsStore = useCampaignsStore();
|
||||||
const contentItemsStore = useContentItemsStore();
|
const contentItemsStore = useContentItemsStore();
|
||||||
|
|
||||||
|
const today = startOfDay(new Date());
|
||||||
|
const viewMode = ref('month');
|
||||||
|
const cursorDate = ref(today);
|
||||||
|
|
||||||
|
const contentStatusMeta = {
|
||||||
|
Draft: { tone: 'production' },
|
||||||
|
'In production': { tone: 'production' },
|
||||||
|
'In approval': { tone: 'approval' },
|
||||||
|
Approved: { tone: 'ready' },
|
||||||
|
Scheduled: { tone: 'ready' },
|
||||||
|
Published: { tone: 'published' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentItemsByCampaignId = computed(() => {
|
||||||
|
const grouped = new Map();
|
||||||
|
|
||||||
|
for (const item of contentItemsStore.items) {
|
||||||
|
const existing = grouped.get(item.campaignId) ?? [];
|
||||||
|
existing.push(item);
|
||||||
|
grouped.set(item.campaignId, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return grouped;
|
||||||
|
});
|
||||||
|
|
||||||
|
const calendarEntries = computed(() => {
|
||||||
|
const campaignEntries = campaignsStore.campaigns
|
||||||
|
.filter(campaign => campaign.endDate || campaign.startDate)
|
||||||
|
.map(campaign => buildCampaignEntry(campaign));
|
||||||
|
|
||||||
|
const contentEntries = contentItemsStore.items
|
||||||
|
.filter(item => item.dueDate)
|
||||||
|
.map(item => buildContentEntry(item));
|
||||||
|
|
||||||
|
return [...campaignEntries, ...contentEntries].sort(sortByDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
const entriesByDay = computed(() => {
|
||||||
|
const grouped = new Map();
|
||||||
|
|
||||||
|
for (const entry of calendarEntries.value) {
|
||||||
|
const existing = grouped.get(entry.dayKey) ?? [];
|
||||||
|
existing.push(entry);
|
||||||
|
grouped.set(entry.dayKey, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return grouped;
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleDays = computed(() => {
|
||||||
|
if (viewMode.value === 'week') {
|
||||||
|
const start = startOfWeek(cursorDate.value);
|
||||||
|
return Array.from({ length: 7 }, (_, index) => buildDay(addDays(start, index), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = startOfWeek(startOfMonth(cursorDate.value));
|
||||||
|
const end = endOfWeek(endOfMonth(cursorDate.value));
|
||||||
|
const days = [];
|
||||||
|
let current = start;
|
||||||
|
|
||||||
|
while (current <= end) {
|
||||||
|
days.push(buildDay(current, current.getMonth() !== cursorDate.value.getMonth()));
|
||||||
|
current = addDays(current, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return days;
|
||||||
|
});
|
||||||
|
|
||||||
|
const weekdayLabels = computed(() => {
|
||||||
|
const base = startOfWeek(cursorDate.value);
|
||||||
|
|
||||||
|
return Array.from({ length: 7 }, (_, index) =>
|
||||||
|
new Intl.DateTimeFormat(locale.value, { weekday: 'short' }).format(addDays(base, index))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const periodLabel = computed(() => {
|
||||||
|
if (viewMode.value === 'week') {
|
||||||
|
const start = startOfWeek(cursorDate.value);
|
||||||
|
const end = addDays(start, 6);
|
||||||
|
const sameMonth = start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear();
|
||||||
|
|
||||||
|
if (sameMonth) {
|
||||||
|
return new Intl.DateTimeFormat(locale.value, {
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}).formatRange(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(locale.value, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}).formatRange(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(locale.value, {
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
}).format(cursorDate.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const upcomingItems = computed(() =>
|
||||||
|
[...contentItemsStore.items].sort((left, right) => {
|
||||||
|
if (!left.dueDate && !right.dueDate) {
|
||||||
|
return left.title.localeCompare(right.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!left.dueDate) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!right.dueDate) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(left.dueDate).getTime() - new Date(right.dueDate).getTime();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const isLoading = computed(() =>
|
||||||
|
contentItemsStore.isLoading || campaignsStore.isLoading
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageError = computed(() =>
|
||||||
|
contentItemsStore.error || campaignsStore.error
|
||||||
|
);
|
||||||
|
|
||||||
|
const isCalendarView = computed(() => viewMode.value === 'month' || viewMode.value === 'week');
|
||||||
|
|
||||||
|
function buildDay(date, isOutsideMonth) {
|
||||||
|
const key = dateKey(date);
|
||||||
|
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
date,
|
||||||
|
entries: entriesByDay.value.get(key) ?? [],
|
||||||
|
isOutsideMonth,
|
||||||
|
isToday: key === dateKey(today),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildContentEntry(item) {
|
||||||
|
const statusMeta = contentStatusMeta[item.status] ?? { tone: 'production' };
|
||||||
|
const campaign = campaignsStore.campaigns.find(candidate => candidate.id === item.campaignId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
type: 'content',
|
||||||
|
title: item.title,
|
||||||
|
subtitle: campaign?.name ?? t('dashboard.labels.unassignedCampaign'),
|
||||||
|
scheduledAt: new Date(item.dueDate),
|
||||||
|
dayKey: dateKey(item.dueDate),
|
||||||
|
timeLabel: formatHour(item.dueDate),
|
||||||
|
tone: statusMeta.tone,
|
||||||
|
route: { name: 'content-item-detail', params: { id: item.id } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCampaignEntry(campaign) {
|
||||||
|
const campaignItems = contentItemsByCampaignId.value.get(campaign.id) ?? [];
|
||||||
|
const approvedCount = campaignItems.filter(item => ['Approved', 'Scheduled', 'Published'].includes(item.status)).length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: campaign.id,
|
||||||
|
type: 'campaign',
|
||||||
|
title: campaign.name,
|
||||||
|
subtitle: campaignItems.length
|
||||||
|
? t('dashboard.campaignProgress', { scheduled: campaignItems.length, approved: approvedCount })
|
||||||
|
: t('dashboard.readiness.missing'),
|
||||||
|
scheduledAt: new Date(campaign.endDate ?? campaign.startDate),
|
||||||
|
dayKey: dateKey(campaign.endDate ?? campaign.startDate),
|
||||||
|
timeLabel: t('dashboard.campaignDeadline'),
|
||||||
|
tone: campaignItems.length ? 'campaign' : 'risk',
|
||||||
|
route: { name: 'campaign-detail', params: { campaignId: campaign.id } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setView(mode) {
|
||||||
|
viewMode.value = mode;
|
||||||
|
|
||||||
|
if (mode === 'month') {
|
||||||
|
cursorDate.value = startOfMonth(cursorDate.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'week') {
|
||||||
|
cursorDate.value = startOfWeek(cursorDate.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftPeriod(direction) {
|
||||||
|
cursorDate.value = viewMode.value === 'month'
|
||||||
|
? addMonths(cursorDate.value, direction)
|
||||||
|
: addDays(cursorDate.value, direction * 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpToToday() {
|
||||||
|
cursorDate.value = viewMode.value === 'month' ? startOfMonth(today) : startOfWeek(today);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDayNumber(date) {
|
||||||
|
return new Intl.DateTimeFormat(locale.value, { day: 'numeric' }).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatHour(value) {
|
||||||
|
return new Intl.DateTimeFormat(locale.value, {
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
}).format(new Date(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDueDate(value) {
|
||||||
|
return value ? new Date(value).toLocaleDateString() : t('contentItems.noDueDate');
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOfDay(value) {
|
||||||
|
const date = new Date(value);
|
||||||
|
date.setHours(0, 0, 0, 0);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOfWeek(value) {
|
||||||
|
const date = startOfDay(value);
|
||||||
|
const day = date.getDay();
|
||||||
|
const diff = day === 0 ? -6 : 1 - day;
|
||||||
|
return addDays(date, diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
function endOfWeek(value) {
|
||||||
|
return addDays(startOfWeek(value), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOfMonth(value) {
|
||||||
|
const date = startOfDay(value);
|
||||||
|
date.setDate(1);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endOfMonth(value) {
|
||||||
|
const date = startOfMonth(value);
|
||||||
|
date.setMonth(date.getMonth() + 1);
|
||||||
|
date.setDate(0);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDays(value, amount) {
|
||||||
|
const date = startOfDay(value);
|
||||||
|
date.setDate(date.getDate() + amount);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMonths(value, amount) {
|
||||||
|
const date = startOfMonth(value);
|
||||||
|
date.setMonth(date.getMonth() + amount);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateKey(value) {
|
||||||
|
const date = new Date(value);
|
||||||
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByDate(left, right) {
|
||||||
|
return left.scheduledAt.getTime() - right.scheduledAt.getTime();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -17,35 +286,150 @@
|
|||||||
<p>{{ t('contentItems.description') }}</p>
|
<p>{{ t('contentItems.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link
|
<div class="view-toggle">
|
||||||
v-if="authStore.isManager || authStore.isProvider"
|
<button
|
||||||
:to="{ name: 'content-item-create' }"
|
class="toggle-button"
|
||||||
class="create-button"
|
:class="{ 'toggle-button-active': viewMode === 'month' }"
|
||||||
>
|
type="button"
|
||||||
{{ t('contentItems.newItem') }}
|
@click="setView('month')"
|
||||||
</router-link>
|
>
|
||||||
|
{{ t('dashboard.month') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="toggle-button"
|
||||||
|
:class="{ 'toggle-button-active': viewMode === 'week' }"
|
||||||
|
type="button"
|
||||||
|
@click="setView('week')"
|
||||||
|
>
|
||||||
|
{{ t('dashboard.week') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="toggle-button"
|
||||||
|
:class="{ 'toggle-button-active': viewMode === 'upcoming' }"
|
||||||
|
type="button"
|
||||||
|
@click="setView('upcoming')"
|
||||||
|
>
|
||||||
|
{{ t('contentItems.upcoming') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="contentItemsStore.isLoading"
|
v-if="isLoading"
|
||||||
class="page-message"
|
class="page-message"
|
||||||
>
|
>
|
||||||
{{ t('contentItems.loading') }}
|
{{ t('contentItems.loading') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="contentItemsStore.error"
|
v-else-if="pageError"
|
||||||
class="page-message error"
|
class="page-message error"
|
||||||
>
|
>
|
||||||
{{ contentItemsStore.error }}
|
{{ pageError }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<article
|
||||||
|
v-else-if="isCalendarView"
|
||||||
|
class="calendar-card"
|
||||||
|
>
|
||||||
|
<div class="calendar-toolbar">
|
||||||
|
<div class="calendar-nav">
|
||||||
|
<button
|
||||||
|
class="icon-button"
|
||||||
|
type="button"
|
||||||
|
@click="shiftPeriod(-1)"
|
||||||
|
>
|
||||||
|
<v-icon :icon="mdiChevronLeft" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="calendar-period">{{ periodLabel }}</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="icon-button"
|
||||||
|
type="button"
|
||||||
|
@click="shiftPeriod(1)"
|
||||||
|
>
|
||||||
|
<v-icon :icon="mdiChevronRight" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="text-button"
|
||||||
|
type="button"
|
||||||
|
@click="jumpToToday"
|
||||||
|
>
|
||||||
|
{{ t('dashboard.today') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calendar-grid calendar-grid-head">
|
||||||
|
<div
|
||||||
|
v-for="label in weekdayLabels"
|
||||||
|
:key="label"
|
||||||
|
class="weekday-label"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="calendar-grid"
|
||||||
|
:class="viewMode === 'week' ? 'calendar-grid-week' : 'calendar-grid-month'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="day in visibleDays"
|
||||||
|
:key="day.key"
|
||||||
|
class="calendar-day"
|
||||||
|
:class="{
|
||||||
|
'calendar-day-outside': day.isOutsideMonth,
|
||||||
|
'calendar-day-today': day.isToday,
|
||||||
|
'calendar-day-week': viewMode === 'week',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="day-number">
|
||||||
|
{{ formatDayNumber(day.date) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="day.entries.length"
|
||||||
|
class="day-entries"
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
v-for="entry in viewMode === 'month' ? day.entries.slice(0, 3) : day.entries"
|
||||||
|
:key="`${entry.type}-${entry.id}`"
|
||||||
|
:to="entry.route"
|
||||||
|
class="calendar-entry"
|
||||||
|
:class="entry.tone"
|
||||||
|
>
|
||||||
|
<span class="entry-time">{{ entry.timeLabel }}</span>
|
||||||
|
<strong>{{ entry.title }}</strong>
|
||||||
|
<span>{{ entry.subtitle }}</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="viewMode === 'month' && day.entries.length > 3"
|
||||||
|
class="entry-more"
|
||||||
|
>
|
||||||
|
{{ t('dashboard.moreItems', { count: day.entries.length - 3 }) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else-if="viewMode === 'week'"
|
||||||
|
class="day-empty"
|
||||||
|
>
|
||||||
|
{{ t('dashboard.emptyPeriod') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-else-if="contentItemsStore.items.length"
|
v-else-if="upcomingItems.length"
|
||||||
class="item-grid"
|
class="item-grid"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-for="item in contentItemsStore.items"
|
v-for="item in upcomingItems"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:to="{ name: 'content-item-detail', params: { id: item.id } }"
|
:to="{ name: 'content-item-detail', params: { id: item.id } }"
|
||||||
class="item-card"
|
class="item-card"
|
||||||
@@ -55,7 +439,7 @@
|
|||||||
<span>{{ item.publicationTargets }}</span>
|
<span>{{ item.publicationTargets }}</span>
|
||||||
<div class="status-row">
|
<div class="status-row">
|
||||||
<em>{{ item.status }}</em>
|
<em>{{ item.status }}</em>
|
||||||
<small>{{ item.dueDate ? new Date(item.dueDate).toLocaleDateString() : t('contentItems.noDueDate') }}</small>
|
<small>{{ formatDueDate(item.dueDate) }}</small>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,10 +480,38 @@
|
|||||||
color: #526178;
|
color: #526178;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-button {
|
.view-toggle {
|
||||||
@apply inline-flex items-center justify-center rounded-full px-5 py-3 text-sm font-bold no-underline transition;
|
@apply inline-flex w-fit rounded-full border p-1;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: rgba(23, 32, 51, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-button,
|
||||||
|
.icon-button,
|
||||||
|
.text-button {
|
||||||
|
@apply inline-flex items-center justify-center rounded-full border px-3 py-2 text-sm font-semibold transition;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: rgba(23, 32, 51, 0.1);
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-button {
|
||||||
|
@apply border-0 bg-transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-button-active {
|
||||||
background: #172033;
|
background: #172033;
|
||||||
color: #fffaf2;
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
@apply h-10 w-10 px-0 py-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button:hover,
|
||||||
|
.text-button:hover,
|
||||||
|
.toggle-button:hover {
|
||||||
|
background: #eef4ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-message,
|
.page-message,
|
||||||
@@ -118,20 +530,138 @@
|
|||||||
color: #b91c1c;
|
color: #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-card {
|
||||||
|
@apply rounded-[1.75rem] border p-4 md:p-5;
|
||||||
|
background: rgba(255, 255, 255, 0.94);
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-toolbar {
|
||||||
|
@apply mb-4 flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav {
|
||||||
|
@apply flex items-center gap-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-period {
|
||||||
|
@apply min-w-0 px-2 text-base font-bold md:text-lg;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid {
|
||||||
|
@apply grid gap-3;
|
||||||
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-head {
|
||||||
|
@apply mb-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-label {
|
||||||
|
@apply px-2 text-xs font-bold uppercase tracking-[0.16em];
|
||||||
|
color: #526178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
@apply min-h-[8.5rem] rounded-[1.25rem] border p-3;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 253, 248, 0.8) 0%, rgba(255, 255, 255, 0.96) 100%);
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-week {
|
||||||
|
@apply min-h-[22rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-outside {
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-today {
|
||||||
|
border-color: rgba(15, 118, 110, 0.22);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(15, 118, 110, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-number {
|
||||||
|
@apply mb-3 text-sm font-bold;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-entries,
|
||||||
|
.item-card {
|
||||||
|
@apply flex flex-col gap-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry {
|
||||||
|
@apply flex flex-col gap-0.5 rounded-[1rem] border px-3 py-2 no-underline transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry:hover,
|
||||||
|
.item-card:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry strong,
|
||||||
|
.item-card strong {
|
||||||
|
@apply text-sm font-bold;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry span {
|
||||||
|
@apply text-xs leading-5;
|
||||||
|
color: #526178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-time {
|
||||||
|
@apply text-[0.7rem] font-bold uppercase tracking-[0.12em];
|
||||||
|
color: #0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry-more,
|
||||||
|
.day-empty {
|
||||||
|
@apply px-1 text-xs font-semibold;
|
||||||
|
color: #526178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.production {
|
||||||
|
background: #fff7ed;
|
||||||
|
border-color: rgba(249, 115, 22, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.approval {
|
||||||
|
background: #eff6ff;
|
||||||
|
border-color: rgba(37, 99, 235, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.ready {
|
||||||
|
background: #ecfdf5;
|
||||||
|
border-color: rgba(5, 150, 105, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.risk {
|
||||||
|
background: #fef2f2;
|
||||||
|
border-color: rgba(220, 38, 38, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.campaign {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: rgba(71, 85, 105, 0.18);
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry.published,
|
||||||
|
.calendar-entry.muted {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-color: rgba(148, 163, 184, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.item-grid {
|
.item-grid {
|
||||||
@apply grid gap-4 md:grid-cols-2 xl:grid-cols-3;
|
@apply grid gap-4 md:grid-cols-2 xl:grid-cols-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-card {
|
.item-card {
|
||||||
@apply flex flex-col gap-4 p-5 no-underline transition;
|
@apply gap-4 p-5 no-underline transition;
|
||||||
}
|
|
||||||
|
|
||||||
.item-card:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-card strong {
|
|
||||||
color: #172033;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-chip {
|
.version-chip {
|
||||||
@@ -143,4 +673,45 @@
|
|||||||
.status-row {
|
.status-row {
|
||||||
@apply flex items-center justify-between gap-3;
|
@apply flex items-center justify-between gap-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
.calendar-grid {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-label {
|
||||||
|
@apply text-[0.65rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
@apply min-h-[7rem] p-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day-week {
|
||||||
|
@apply min-h-[18rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-entry {
|
||||||
|
@apply px-2 py-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.calendar-toolbar {
|
||||||
|
@apply items-stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav {
|
||||||
|
@apply justify-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-head,
|
||||||
|
.calendar-grid {
|
||||||
|
min-width: 46rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-card {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -26,14 +26,32 @@
|
|||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
class="site-login"
|
class="site-login"
|
||||||
to="/login"
|
:to="authLink"
|
||||||
>
|
>
|
||||||
Login
|
{{ authLabel }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||||
|
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const authLink = computed(() =>
|
||||||
|
authStore.isAuthenticated
|
||||||
|
? '/app/dashboard'
|
||||||
|
: '/login'
|
||||||
|
);
|
||||||
|
const authLabel = computed(() =>
|
||||||
|
authStore.isAuthenticated
|
||||||
|
? 'Open app'
|
||||||
|
: 'Login'
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.site-menu {
|
.site-menu {
|
||||||
@apply sticky top-0 z-30 w-full;
|
@apply sticky top-0 z-30 w-full;
|
||||||
|
|||||||
@@ -224,7 +224,6 @@
|
|||||||
<strong>{{ workspace.name }}</strong>
|
<strong>{{ workspace.name }}</strong>
|
||||||
<span>{{ workspace.timeZone }}</span>
|
<span>{{ workspace.timeZone }}</span>
|
||||||
</div>
|
</div>
|
||||||
<small>{{ workspace.slug }}</small>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-if="!organization.workspaces?.length"
|
v-if="!organization.workspaces?.length"
|
||||||
|
|||||||
@@ -13,18 +13,10 @@
|
|||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
slug: '',
|
|
||||||
timeZone: computedDefaultTimeZone(),
|
timeZone: computedDefaultTimeZone(),
|
||||||
});
|
});
|
||||||
const formError = ref(null);
|
const formError = ref(null);
|
||||||
|
|
||||||
const previewSlug = computed(() => {
|
|
||||||
if (form.slug.trim()) {
|
|
||||||
return slugify(form.slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
return slugify(form.name);
|
|
||||||
});
|
|
||||||
const selectedOrganizationId = computed({
|
const selectedOrganizationId = computed({
|
||||||
get: () => organizationStore.selectedOrganizationId,
|
get: () => organizationStore.selectedOrganizationId,
|
||||||
set: value => organizationStore.setSelectedOrganization(value),
|
set: value => organizationStore.setSelectedOrganization(value),
|
||||||
@@ -34,15 +26,6 @@
|
|||||||
return workspaceStore.activeWorkspace?.timeZone || 'America/Montreal';
|
return workspaceStore.activeWorkspace?.timeZone || 'America/Montreal';
|
||||||
}
|
}
|
||||||
|
|
||||||
function slugify(value) {
|
|
||||||
return (value ?? '')
|
|
||||||
.toLowerCase()
|
|
||||||
.trim()
|
|
||||||
.replace(/[^a-z0-9]+/g, '-')
|
|
||||||
.replace(/^-+|-+$/g, '')
|
|
||||||
.slice(0, 80);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function submitForm() {
|
async function submitForm() {
|
||||||
if (workspaceStore.isCreating) {
|
if (workspaceStore.isCreating) {
|
||||||
return;
|
return;
|
||||||
@@ -51,10 +34,9 @@
|
|||||||
formError.value = null;
|
formError.value = null;
|
||||||
|
|
||||||
const name = form.name.trim();
|
const name = form.name.trim();
|
||||||
const slug = slugify(form.slug || form.name);
|
|
||||||
const timeZone = form.timeZone.trim();
|
const timeZone = form.timeZone.trim();
|
||||||
|
|
||||||
if (!name || !slug || !timeZone || !selectedOrganizationId.value) {
|
if (!name || !timeZone || !selectedOrganizationId.value) {
|
||||||
formError.value = t('workspaceCreate.errors.required');
|
formError.value = t('workspaceCreate.errors.required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -63,7 +45,6 @@
|
|||||||
await workspaceStore.createWorkspace({
|
await workspaceStore.createWorkspace({
|
||||||
organizationId: selectedOrganizationId.value,
|
organizationId: selectedOrganizationId.value,
|
||||||
name,
|
name,
|
||||||
slug,
|
|
||||||
timeZone,
|
timeZone,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -86,12 +67,6 @@
|
|||||||
<h1>{{ t('workspaceCreate.title') }}</h1>
|
<h1>{{ t('workspaceCreate.title') }}</h1>
|
||||||
<p>{{ t('workspaceCreate.description') }}</p>
|
<p>{{ t('workspaceCreate.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-note">
|
|
||||||
<strong>{{ t('workspaceCreate.previewTitle') }}</strong>
|
|
||||||
<span>{{ t('workspaceCreate.previewDescription') }}</span>
|
|
||||||
<code>{{ previewSlug || 'workspace-slug' }}</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article class="create-card">
|
<article class="create-card">
|
||||||
@@ -137,17 +112,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="field">
|
|
||||||
<span>{{ t('workspaceCreate.fields.slug') }}</span>
|
|
||||||
<input
|
|
||||||
v-model="form.slug"
|
|
||||||
type="text"
|
|
||||||
:placeholder="t('workspaceCreate.fields.slugPlaceholder')"
|
|
||||||
:disabled="workspaceStore.isCreating"
|
|
||||||
/>
|
|
||||||
<small>{{ t('workspaceCreate.slugHint', { slug: previewSlug || 'workspace-slug' }) }}</small>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="field">
|
<label class="field">
|
||||||
<span>{{ t('workspaceCreate.fields.timeZone') }}</span>
|
<span>{{ t('workspaceCreate.fields.timeZone') }}</span>
|
||||||
<TimeZoneSelect
|
<TimeZoneSelect
|
||||||
@@ -183,12 +147,7 @@
|
|||||||
@apply mx-auto flex w-full max-w-6xl flex-col gap-6 px-5 py-8 md:px-8;
|
@apply mx-auto flex w-full max-w-6xl flex-col gap-6 px-5 py-8 md:px-8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero {
|
|
||||||
@apply grid gap-4 lg:grid-cols-[minmax(0,1.3fr)_minmax(18rem,0.8fr)];
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-copy,
|
.hero-copy,
|
||||||
.hero-note,
|
|
||||||
.create-card {
|
.create-card {
|
||||||
@apply rounded-[1.75rem] border;
|
@apply rounded-[1.75rem] border;
|
||||||
border-color: rgba(23, 32, 51, 0.08);
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
@@ -213,33 +172,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hero-copy p,
|
.hero-copy p,
|
||||||
.hero-note span,
|
|
||||||
.card-header span,
|
.card-header span,
|
||||||
.field small {
|
.field small {
|
||||||
@apply text-sm leading-6;
|
@apply text-sm leading-6;
|
||||||
color: #526178;
|
color: #526178;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-note,
|
|
||||||
.create-card {
|
.create-card {
|
||||||
@apply flex flex-col gap-4 p-6;
|
@apply flex flex-col gap-4 p-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-note strong,
|
|
||||||
.card-header strong {
|
.card-header strong {
|
||||||
color: #172033;
|
color: #172033;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-note strong {
|
|
||||||
@apply text-xl font-black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-note code {
|
|
||||||
@apply rounded-[1rem] px-3 py-2 text-sm;
|
|
||||||
background: rgba(23, 32, 51, 0.06);
|
|
||||||
color: #172033;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
@apply flex flex-col gap-2;
|
@apply flex flex-col gap-2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-left {
|
.side-menu-left {
|
||||||
@apply justify-start;
|
@apply justify-start pl-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu-right {
|
.side-menu-right {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||||
@@ -13,8 +13,6 @@
|
|||||||
mdiBellOutline,
|
mdiBellOutline,
|
||||||
mdiCalendarMonthOutline,
|
mdiCalendarMonthOutline,
|
||||||
mdiChevronDown,
|
mdiChevronDown,
|
||||||
mdiCogOutline,
|
|
||||||
mdiFileDocumentOutline,
|
|
||||||
mdiFolderOutline,
|
mdiFolderOutline,
|
||||||
mdiHomeOutline,
|
mdiHomeOutline,
|
||||||
mdiImageMultipleOutline,
|
mdiImageMultipleOutline,
|
||||||
@@ -45,14 +43,13 @@
|
|||||||
|
|
||||||
const notificationsRef = ref(null);
|
const notificationsRef = ref(null);
|
||||||
const searchRef = ref(null);
|
const searchRef = ref(null);
|
||||||
|
const collapsedSearchInputRef = ref(null);
|
||||||
|
const collapsedSearchPanelStyle = ref({});
|
||||||
|
|
||||||
const primaryLinks = [
|
const primaryLinks = [
|
||||||
{ to: '/app/dashboard', labelKey: 'nav.overview', icon: mdiHomeOutline },
|
{ to: '/app/dashboard', labelKey: 'nav.overview', icon: mdiHomeOutline },
|
||||||
{ to: '/app/workspace', labelKey: 'nav.workspacePlan', icon: mdiCalendarMonthOutline },
|
|
||||||
{ to: '/app/media-library', labelKey: 'nav.mediaLibrary', icon: mdiImageMultipleOutline },
|
{ to: '/app/media-library', labelKey: 'nav.mediaLibrary', icon: mdiImageMultipleOutline },
|
||||||
{ to: '/app/my-feedback', labelKey: 'nav.myFeedback', icon: mdiBugOutline },
|
|
||||||
{ to: '/app/feedback', labelKey: 'nav.feedbackReview', icon: mdiBugOutline, roles: ['developer'] },
|
{ to: '/app/feedback', labelKey: 'nav.feedbackReview', icon: mdiBugOutline, roles: ['developer'] },
|
||||||
{ to: '/app/workspace-settings', labelKey: 'nav.settings', icon: mdiCogOutline },
|
|
||||||
];
|
];
|
||||||
const visiblePrimaryLinks = computed(() =>
|
const visiblePrimaryLinks = computed(() =>
|
||||||
primaryLinks.filter(link => !link.roles || authStore.hasAnyRole(link.roles))
|
primaryLinks.filter(link => !link.roles || authStore.hasAnyRole(link.roles))
|
||||||
@@ -102,6 +99,9 @@
|
|||||||
campaignResults.value.length > 0 || contentResults.value.length > 0
|
campaignResults.value.length > 0 || contentResults.value.length > 0
|
||||||
);
|
);
|
||||||
const isSearchOpen = computed(() => isSearchFocused.value && normalizedSearchQuery.value.length > 0);
|
const isSearchOpen = computed(() => isSearchFocused.value && normalizedSearchQuery.value.length > 0);
|
||||||
|
const isSearchPanelOpen = computed(() =>
|
||||||
|
isSearchFocused.value && (!props.isExpanded || normalizedSearchQuery.value.length > 0)
|
||||||
|
);
|
||||||
|
|
||||||
const notificationTitleMap = computed(() => ({
|
const notificationTitleMap = computed(() => ({
|
||||||
'approval.requested': t('notifications.events.approvalRequested'),
|
'approval.requested': t('notifications.events.approvalRequested'),
|
||||||
@@ -127,6 +127,34 @@
|
|||||||
isNotificationsOpen.value = !isNotificationsOpen.value;
|
isNotificationsOpen.value = !isNotificationsOpen.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCollapsedSearchPanelPosition() {
|
||||||
|
if (props.isExpanded || !searchRef.value) {
|
||||||
|
collapsedSearchPanelStyle.value = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = searchRef.value.getBoundingClientRect();
|
||||||
|
const left = rect.right + 12;
|
||||||
|
const availableWidth = Math.max(240, window.innerWidth - left - 12);
|
||||||
|
|
||||||
|
collapsedSearchPanelStyle.value = {
|
||||||
|
left: `${left}px`,
|
||||||
|
top: `${Math.max(12, rect.top)}px`,
|
||||||
|
width: `${Math.min(352, availableWidth)}px`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openCollapsedSearch() {
|
||||||
|
if (props.isExpanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSearchFocused.value = true;
|
||||||
|
updateCollapsedSearchPanelPosition();
|
||||||
|
await nextTick();
|
||||||
|
collapsedSearchInputRef.value?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
function formatNotificationTitle(notification) {
|
function formatNotificationTitle(notification) {
|
||||||
return notificationTitleMap.value[notification.eventType] ?? notification.message;
|
return notificationTitleMap.value[notification.eventType] ?? notification.message;
|
||||||
}
|
}
|
||||||
@@ -181,12 +209,35 @@
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(isSearchPanelOpen, isOpen => {
|
||||||
|
if (isOpen) {
|
||||||
|
updateCollapsedSearchPanelPosition();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.isExpanded,
|
||||||
|
isExpanded => {
|
||||||
|
if (isExpanded) {
|
||||||
|
collapsedSearchPanelStyle.value = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isNotificationsOpen.value = false;
|
||||||
|
updateCollapsedSearchPanelPosition();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleDocumentClick);
|
document.addEventListener('click', handleDocumentClick);
|
||||||
|
window.addEventListener('resize', updateCollapsedSearchPanelPosition);
|
||||||
|
window.addEventListener('scroll', updateCollapsedSearchPanelPosition, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener('click', handleDocumentClick);
|
document.removeEventListener('click', handleDocumentClick);
|
||||||
|
window.removeEventListener('resize', updateCollapsedSearchPanelPosition);
|
||||||
|
window.removeEventListener('scroll', updateCollapsedSearchPanelPosition, true);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -222,6 +273,8 @@
|
|||||||
<label
|
<label
|
||||||
class="sidebar-search"
|
class="sidebar-search"
|
||||||
:class="{ 'sidebar-search-open': isSearchOpen }"
|
:class="{ 'sidebar-search-open': isSearchOpen }"
|
||||||
|
:title="!isExpanded ? 'Search' : null"
|
||||||
|
@click="openCollapsedSearch"
|
||||||
>
|
>
|
||||||
<v-icon
|
<v-icon
|
||||||
:icon="mdiMagnify"
|
:icon="mdiMagnify"
|
||||||
@@ -238,9 +291,28 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isSearchOpen"
|
v-if="isSearchPanelOpen"
|
||||||
class="sidebar-floating-panel"
|
class="sidebar-floating-panel"
|
||||||
|
:class="{ 'sidebar-search-panel-collapsed': !isExpanded }"
|
||||||
|
:style="!isExpanded ? collapsedSearchPanelStyle : null"
|
||||||
>
|
>
|
||||||
|
<label
|
||||||
|
v-if="!isExpanded"
|
||||||
|
class="sidebar-search sidebar-search-panel-input"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
:icon="mdiMagnify"
|
||||||
|
class="sidebar-search-icon"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
ref="collapsedSearchInputRef"
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="search"
|
||||||
|
class="sidebar-search-input"
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="campaignResults.length"
|
v-if="campaignResults.length"
|
||||||
class="sidebar-search-group"
|
class="sidebar-search-group"
|
||||||
@@ -274,7 +346,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="!hasSearchResults"
|
v-if="normalizedSearchQuery.length > 0 && !hasSearchResults"
|
||||||
class="sidebar-search-empty"
|
class="sidebar-search-empty"
|
||||||
>
|
>
|
||||||
No results found.
|
No results found.
|
||||||
@@ -383,7 +455,7 @@
|
|||||||
:title="!isExpanded ? t('nav.content') : null"
|
:title="!isExpanded ? t('nav.content') : null"
|
||||||
>
|
>
|
||||||
<span class="sidebar-link-main">
|
<span class="sidebar-link-main">
|
||||||
<v-icon :icon="mdiFileDocumentOutline" />
|
<v-icon :icon="mdiCalendarMonthOutline" />
|
||||||
<span
|
<span
|
||||||
v-if="isExpanded"
|
v-if="isExpanded"
|
||||||
class="sidebar-link-label"
|
class="sidebar-link-label"
|
||||||
@@ -411,6 +483,7 @@
|
|||||||
class="sidebar-link sidebar-link-section"
|
class="sidebar-link sidebar-link-section"
|
||||||
active-class="sidebar-link-active"
|
active-class="sidebar-link-active"
|
||||||
:title="!isExpanded ? t('nav.campaigns') : null"
|
:title="!isExpanded ? t('nav.campaigns') : null"
|
||||||
|
@click="toggleSection('campaigns')"
|
||||||
>
|
>
|
||||||
<span class="sidebar-link-main">
|
<span class="sidebar-link-main">
|
||||||
<v-icon :icon="mdiFolderOutline" />
|
<v-icon :icon="mdiFolderOutline" />
|
||||||
@@ -420,6 +493,12 @@
|
|||||||
>
|
>
|
||||||
{{ t('nav.campaigns') }}
|
{{ t('nav.campaigns') }}
|
||||||
</span>
|
</span>
|
||||||
|
<v-icon
|
||||||
|
v-if="isExpanded"
|
||||||
|
:icon="mdiChevronDown"
|
||||||
|
class="sidebar-chevron"
|
||||||
|
:class="{ 'sidebar-chevron-open': openSections.campaigns }"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
@@ -431,19 +510,6 @@
|
|||||||
>
|
>
|
||||||
<v-icon :icon="mdiPlus" />
|
<v-icon :icon="mdiPlus" />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<button
|
|
||||||
v-if="isExpanded"
|
|
||||||
class="sidebar-section-toggle"
|
|
||||||
type="button"
|
|
||||||
@click="toggleSection('campaigns')"
|
|
||||||
>
|
|
||||||
<v-icon
|
|
||||||
:icon="mdiChevronDown"
|
|
||||||
class="sidebar-chevron"
|
|
||||||
:class="{ 'sidebar-chevron-open': openSections.campaigns }"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -484,6 +550,7 @@
|
|||||||
class="sidebar-link sidebar-link-section"
|
class="sidebar-link sidebar-link-section"
|
||||||
active-class="sidebar-link-active"
|
active-class="sidebar-link-active"
|
||||||
:title="!isExpanded ? t('nav.channels') : null"
|
:title="!isExpanded ? t('nav.channels') : null"
|
||||||
|
@click="toggleSection('channels')"
|
||||||
>
|
>
|
||||||
<span class="sidebar-link-main">
|
<span class="sidebar-link-main">
|
||||||
<v-icon :icon="mdiLan" />
|
<v-icon :icon="mdiLan" />
|
||||||
@@ -493,6 +560,12 @@
|
|||||||
>
|
>
|
||||||
{{ t('nav.channels') }}
|
{{ t('nav.channels') }}
|
||||||
</span>
|
</span>
|
||||||
|
<v-icon
|
||||||
|
v-if="isExpanded"
|
||||||
|
:icon="mdiChevronDown"
|
||||||
|
class="sidebar-chevron"
|
||||||
|
:class="{ 'sidebar-chevron-open': openSections.channels }"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
@@ -504,19 +577,6 @@
|
|||||||
>
|
>
|
||||||
<v-icon :icon="mdiPlus" />
|
<v-icon :icon="mdiPlus" />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<button
|
|
||||||
v-if="isExpanded"
|
|
||||||
class="sidebar-section-toggle"
|
|
||||||
type="button"
|
|
||||||
@click="toggleSection('channels')"
|
|
||||||
>
|
|
||||||
<v-icon
|
|
||||||
:icon="mdiChevronDown"
|
|
||||||
class="sidebar-chevron"
|
|
||||||
:class="{ 'sidebar-chevron-open': openSections.channels }"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -566,20 +626,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar-scroll {
|
.app-sidebar-scroll {
|
||||||
@apply flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto pb-4 pt-3 pr-3;
|
@apply flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto pb-4 pt-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-block {
|
.brand-block {
|
||||||
@apply flex items-center gap-3;
|
@apply flex items-center gap-3 pb-4;
|
||||||
|
border-bottom: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-link {
|
.brand-link {
|
||||||
@apply flex items-center gap-3 no-underline;
|
@apply flex min-w-0 items-center gap-3 no-underline;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.brand-link-collapsed {
|
||||||
|
@apply w-full justify-center;
|
||||||
|
}
|
||||||
|
|
||||||
.brand-mark {
|
.brand-mark {
|
||||||
@apply flex h-11 w-11 items-center justify-center rounded-2xl text-lg font-black;
|
@apply flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-[1.1rem] text-xl font-black;
|
||||||
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
||||||
color: #fffaf2;
|
color: #fffaf2;
|
||||||
}
|
}
|
||||||
@@ -621,7 +686,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-search-icon {
|
.sidebar-search-icon {
|
||||||
@apply text-xl;
|
@apply h-5 w-5 flex-shrink-0 text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-search-input {
|
.sidebar-search-input {
|
||||||
@@ -641,6 +706,14 @@
|
|||||||
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-search-panel-collapsed {
|
||||||
|
@apply fixed right-auto top-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-search-panel-input {
|
||||||
|
@apply bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-search-group {
|
.sidebar-search-group {
|
||||||
@apply flex flex-col gap-1;
|
@apply flex flex-col gap-1;
|
||||||
}
|
}
|
||||||
@@ -761,18 +834,12 @@
|
|||||||
@apply truncate;
|
@apply truncate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-section-toggle {
|
|
||||||
@apply flex h-11 w-11 items-center justify-center rounded-[1rem] transition-colors;
|
|
||||||
color: #526178;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-section-action {
|
.sidebar-section-action {
|
||||||
@apply flex h-11 w-11 items-center justify-center rounded-[1rem] transition-colors no-underline;
|
@apply ml-auto flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-[1rem] transition-colors no-underline;
|
||||||
color: #526178;
|
color: #526178;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-section-action:hover,
|
.sidebar-section-action:hover {
|
||||||
.sidebar-section-toggle:hover {
|
|
||||||
background: rgba(23, 32, 51, 0.06);
|
background: rgba(23, 32, 51, 0.06);
|
||||||
color: #172033;
|
color: #172033;
|
||||||
}
|
}
|
||||||
@@ -785,8 +852,9 @@
|
|||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-link :deep(.v-icon) {
|
.sidebar-link :deep(.v-icon),
|
||||||
@apply text-xl;
|
.sidebar-section-action :deep(.v-icon) {
|
||||||
|
@apply h-5 w-5 flex-shrink-0 text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-sublist {
|
.sidebar-sublist {
|
||||||
@@ -818,10 +886,30 @@
|
|||||||
@apply w-[5.5rem] px-3;
|
@apply w-[5.5rem] px-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-sidebar-collapsed .brand-block {
|
||||||
|
@apply justify-center;
|
||||||
|
}
|
||||||
|
|
||||||
.app-sidebar-collapsed .sidebar-search {
|
.app-sidebar-collapsed .sidebar-search {
|
||||||
@apply justify-center px-0;
|
@apply justify-center px-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-sidebar-collapsed .sidebar-link {
|
||||||
|
@apply justify-center px-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-collapsed .sidebar-link-main {
|
||||||
|
@apply justify-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-collapsed .sidebar-search:hover {
|
||||||
|
background: rgba(23, 32, 51, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-collapsed .sidebar-search-panel-input {
|
||||||
|
@apply justify-start px-4;
|
||||||
|
}
|
||||||
|
|
||||||
.app-sidebar-collapsed .sidebar-floating-panel {
|
.app-sidebar-collapsed .sidebar-floating-panel {
|
||||||
left: calc(100% + 0.75rem);
|
left: calc(100% + 0.75rem);
|
||||||
right: auto;
|
right: auto;
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||||
import { useLanguageStore } from '@/stores/languageStore.js';
|
import { useLanguageStore } from '@/stores/languageStore.js';
|
||||||
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
|
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
|
||||||
import { mdiChevronDown } from '@mdi/js';
|
import {
|
||||||
|
mdiAccountCircleOutline,
|
||||||
|
mdiBugOutline,
|
||||||
|
mdiChevronDown,
|
||||||
|
mdiLogout,
|
||||||
|
mdiTranslate,
|
||||||
|
} from '@mdi/js';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isExpanded: {
|
isExpanded: {
|
||||||
@@ -16,7 +22,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const languageStore = useLanguageStore();
|
const languageStore = useLanguageStore();
|
||||||
const userProfileStore = useUserProfileStore();
|
const userProfileStore = useUserProfileStore();
|
||||||
@@ -24,16 +30,13 @@
|
|||||||
const userMenuRef = ref(null);
|
const userMenuRef = ref(null);
|
||||||
|
|
||||||
function toggleUserMenu() {
|
function toggleUserMenu() {
|
||||||
if (!props.isExpanded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUserMenuOpen.value = !isUserMenuOpen.value;
|
isUserMenuOpen.value = !isUserMenuOpen.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLanguage() {
|
function toggleLanguage() {
|
||||||
const nextLocale = languageStore.locale === 'en' ? 'fr' : 'en';
|
const nextLocale = locale.value === 'en' ? 'fr' : 'en';
|
||||||
languageStore.setLocale(nextLocale);
|
languageStore.setLocale(nextLocale);
|
||||||
|
locale.value = nextLocale;
|
||||||
isUserMenuOpen.value = false;
|
isUserMenuOpen.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +45,11 @@
|
|||||||
await router.push({ name: 'settings-user-information' });
|
await router.push({ name: 'settings-user-information' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openMyFeedback() {
|
||||||
|
isUserMenuOpen.value = false;
|
||||||
|
await router.push({ name: 'my-feedback' });
|
||||||
|
}
|
||||||
|
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
isUserMenuOpen.value = false;
|
isUserMenuOpen.value = false;
|
||||||
authStore.logout();
|
authStore.logout();
|
||||||
@@ -75,6 +83,7 @@
|
|||||||
<div
|
<div
|
||||||
ref="userMenuRef"
|
ref="userMenuRef"
|
||||||
class="sidebar-workspace sidebar-workspace-bottom"
|
class="sidebar-workspace sidebar-workspace-bottom"
|
||||||
|
:class="{ 'sidebar-workspace-collapsed': !isExpanded }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="sidebar-workspace-trigger"
|
class="sidebar-workspace-trigger"
|
||||||
@@ -102,29 +111,42 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="isExpanded && isUserMenuOpen"
|
v-if="isUserMenuOpen"
|
||||||
class="sidebar-workspace-menu"
|
class="sidebar-workspace-menu"
|
||||||
>
|
>
|
||||||
|
<button
|
||||||
|
class="sidebar-workspace-option"
|
||||||
|
type="button"
|
||||||
|
@click="openMyFeedback"
|
||||||
|
>
|
||||||
|
<v-icon :icon="mdiBugOutline" />
|
||||||
|
<span>{{ t('nav.myFeedback') }}</span>
|
||||||
|
</button>
|
||||||
|
<div class="sidebar-workspace-separator" />
|
||||||
<button
|
<button
|
||||||
class="sidebar-workspace-option"
|
class="sidebar-workspace-option"
|
||||||
type="button"
|
type="button"
|
||||||
@click="openProfile"
|
@click="openProfile"
|
||||||
>
|
>
|
||||||
{{ t('nav.profile') }}
|
<v-icon :icon="mdiAccountCircleOutline" />
|
||||||
|
<span>{{ t('nav.profile') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="sidebar-workspace-option"
|
class="sidebar-workspace-option"
|
||||||
type="button"
|
type="button"
|
||||||
@click="toggleLanguage"
|
@click="toggleLanguage"
|
||||||
>
|
>
|
||||||
{{ t('nav.language') }}
|
<v-icon :icon="mdiTranslate" />
|
||||||
|
<span>{{ t('nav.language') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
<div class="sidebar-workspace-separator" />
|
||||||
<button
|
<button
|
||||||
class="sidebar-workspace-option sidebar-workspace-option-danger"
|
class="sidebar-workspace-option sidebar-workspace-option-danger"
|
||||||
type="button"
|
type="button"
|
||||||
@click="handleLogout"
|
@click="handleLogout"
|
||||||
>
|
>
|
||||||
{{ t('nav.signOut') }}
|
<v-icon :icon="mdiLogout" />
|
||||||
|
<span>{{ t('nav.signOut') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -172,16 +194,43 @@
|
|||||||
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-collapsed {
|
||||||
|
@apply items-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-collapsed .sidebar-workspace-trigger {
|
||||||
|
@apply h-11 w-11 justify-center rounded-[1rem] p-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-collapsed .sidebar-workspace-menu {
|
||||||
|
@apply left-[calc(100%+0.75rem)] right-auto w-56;
|
||||||
|
bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-workspace-option {
|
.sidebar-workspace-option {
|
||||||
@apply rounded-[0.95rem] px-4 py-3 text-left text-sm font-semibold transition-colors;
|
@apply flex items-center gap-3 rounded-[0.95rem] px-4 py-3 text-left text-sm font-semibold transition-colors;
|
||||||
color: #172033;
|
color: #172033;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-option .v-icon {
|
||||||
|
@apply text-base;
|
||||||
|
color: #5d6b82;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-workspace-option:hover {
|
.sidebar-workspace-option:hover {
|
||||||
background: rgba(23, 32, 51, 0.05);
|
background: rgba(23, 32, 51, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-separator {
|
||||||
|
@apply my-1;
|
||||||
|
border-top: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-workspace-option-danger {
|
.sidebar-workspace-option-danger {
|
||||||
color: #b91c1c;
|
color: #b91c1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-workspace-option-danger .v-icon {
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -93,6 +93,16 @@
|
|||||||
await router.push({ name: 'workspace-create' });
|
await router.push({ name: 'workspace-create' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openWorkspaceSettings(workspaceId) {
|
||||||
|
if (workspaceId) {
|
||||||
|
workspaceStore.setActiveWorkspace(workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
isWorkspaceMenuOpen.value = false;
|
||||||
|
isOrganizationListOpen.value = false;
|
||||||
|
await router.push({ name: 'workspace-settings' });
|
||||||
|
}
|
||||||
|
|
||||||
async function openOrganizationSettings(organizationId) {
|
async function openOrganizationSettings(organizationId) {
|
||||||
isWorkspaceMenuOpen.value = false;
|
isWorkspaceMenuOpen.value = false;
|
||||||
isOrganizationListOpen.value = false;
|
isOrganizationListOpen.value = false;
|
||||||
@@ -143,23 +153,38 @@
|
|||||||
v-if="isWorkspaceMenuOpen"
|
v-if="isWorkspaceMenuOpen"
|
||||||
class="user-menu"
|
class="user-menu"
|
||||||
>
|
>
|
||||||
<button
|
<div
|
||||||
v-for="workspace in visibleWorkspaces"
|
v-for="workspace in visibleWorkspaces"
|
||||||
:key="workspace.id"
|
:key="workspace.id"
|
||||||
class="user-menu-item"
|
class="workspace-menu-row"
|
||||||
:class="{ 'user-menu-item-active': workspace.id === workspaceStore.activeWorkspaceId }"
|
:class="{ 'user-menu-item-active': workspace.id === workspaceStore.activeWorkspaceId }"
|
||||||
@click="chooseWorkspace(workspace.id)"
|
|
||||||
>
|
>
|
||||||
<AppAvatar
|
<button
|
||||||
:name="workspace.name"
|
class="user-menu-item workspace-menu-select"
|
||||||
:src="workspace.logoUrl"
|
type="button"
|
||||||
size="sm"
|
@click="chooseWorkspace(workspace.id)"
|
||||||
/>
|
>
|
||||||
<span class="user-menu-item-copy">
|
<AppAvatar
|
||||||
<span>{{ workspace.name }}</span>
|
:name="workspace.name"
|
||||||
<small>{{ workspace.timeZone }}</small>
|
:src="workspace.logoUrl"
|
||||||
</span>
|
size="sm"
|
||||||
</button>
|
/>
|
||||||
|
<span class="user-menu-item-copy">
|
||||||
|
<span>{{ workspace.name }}</span>
|
||||||
|
<small>{{ workspace.timeZone }}</small>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="canManageWorkspaces"
|
||||||
|
class="workspace-settings-button"
|
||||||
|
type="button"
|
||||||
|
:aria-label="t('workspaceSelector.workspaceSettings')"
|
||||||
|
@click="openWorkspaceSettings(workspace.id)"
|
||||||
|
>
|
||||||
|
<v-icon :icon="mdiCogOutline" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="canManageWorkspaces"
|
v-if="canManageWorkspaces"
|
||||||
@@ -270,7 +295,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-menu {
|
.user-menu {
|
||||||
@apply absolute right-0 top-[calc(100%+0.75rem)] flex max-h-[80vh] min-w-[17rem] flex-col gap-1 overflow-y-auto rounded-[1.25rem] border p-2;
|
@apply absolute left-0 top-[calc(100%+0.75rem)] flex max-h-[80vh] min-w-[17rem] flex-col gap-1 overflow-y-auto rounded-[1.25rem] border p-2;
|
||||||
|
width: max(100%, 17rem);
|
||||||
|
max-width: min(24rem, calc(100vw - 2rem));
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
background: #fffdf8;
|
background: #fffdf8;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
@@ -284,7 +311,8 @@
|
|||||||
color: #172033;
|
color: #172033;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-menu-item:hover {
|
.user-menu-item:hover,
|
||||||
|
.workspace-menu-row:hover {
|
||||||
background: rgba(23, 32, 51, 0.06);
|
background: rgba(23, 32, 51, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +321,33 @@
|
|||||||
color: #c2410c;
|
color: #c2410c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-menu-row {
|
||||||
|
@apply flex min-w-0 items-center rounded-[0.9rem] transition-colors;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-menu-select {
|
||||||
|
@apply min-w-0 flex-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-menu-select:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-settings-button {
|
||||||
|
@apply mr-2 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-colors;
|
||||||
|
color: #526178;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-settings-button:hover {
|
||||||
|
background: rgba(23, 32, 51, 0.1);
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-settings-button :deep(.v-icon) {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.user-menu-item-copy {
|
.user-menu-item-copy {
|
||||||
@apply flex min-w-0 flex-1 flex-col gap-0.5;
|
@apply flex min-w-0 flex-1 flex-col gap-0.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,24 +42,20 @@
|
|||||||
"createAction": "Add workspace",
|
"createAction": "Add workspace",
|
||||||
"organizationLabel": "Organization",
|
"organizationLabel": "Organization",
|
||||||
"organizationSettings": "Organization settings",
|
"organizationSettings": "Organization settings",
|
||||||
"noOrganization": "No organization"
|
"noOrganization": "No organization",
|
||||||
|
"workspaceSettings": "Workspace settings"
|
||||||
},
|
},
|
||||||
"workspaceCreate": {
|
"workspaceCreate": {
|
||||||
"eyebrow": "Workspace",
|
"eyebrow": "Workspace",
|
||||||
"title": "Create a new workspace",
|
"title": "Create a new workspace",
|
||||||
"description": "Set up a new workspace with its own slug, timezone, members, workflow, and connectors.",
|
"description": "Set up a new workspace with its own timezone, members, workflow, and connectors.",
|
||||||
"previewTitle": "Workspace URL",
|
|
||||||
"previewDescription": "The slug becomes the stable identifier used for the workspace.",
|
|
||||||
"formTitle": "Workspace details",
|
"formTitle": "Workspace details",
|
||||||
"formDescription": "Start with the core fields now. Members, workflow, and connectors can be configured right after creation.",
|
"formDescription": "Start with the core fields now. Members, workflow, and connectors can be configured right after creation.",
|
||||||
"createAction": "Create workspace",
|
"createAction": "Create workspace",
|
||||||
"slugHint": "Workspace slug preview: {slug}",
|
|
||||||
"fields": {
|
"fields": {
|
||||||
"name": "Workspace name",
|
"name": "Workspace name",
|
||||||
"namePlaceholder": "Northwind Studio",
|
"namePlaceholder": "Northwind Studio",
|
||||||
"organization": "Organization",
|
"organization": "Organization",
|
||||||
"slug": "Workspace slug",
|
|
||||||
"slugPlaceholder": "northwind-studio",
|
|
||||||
"timeZone": "Time zone"
|
"timeZone": "Time zone"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -131,7 +127,7 @@
|
|||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"overview": "Overview",
|
"overview": "Overview",
|
||||||
"workspacePlan": "Content",
|
"workspacePlan": "Calendar",
|
||||||
"mediaLibrary": "Media Library",
|
"mediaLibrary": "Media Library",
|
||||||
"myFeedback": "My Feedback",
|
"myFeedback": "My Feedback",
|
||||||
"feedbackReview": "Feedback Review",
|
"feedbackReview": "Feedback Review",
|
||||||
@@ -493,6 +489,7 @@
|
|||||||
"description": "Reviewable units with assets, copy, and approval status inside the active workspace.",
|
"description": "Reviewable units with assets, copy, and approval status inside the active workspace.",
|
||||||
"newItem": "New content item",
|
"newItem": "New content item",
|
||||||
"createTitle": "Create content item",
|
"createTitle": "Create content item",
|
||||||
|
"upcoming": "Upcoming",
|
||||||
"loading": "Loading content items...",
|
"loading": "Loading content items...",
|
||||||
"empty": "No content items are available for the active workspace.",
|
"empty": "No content items are available for the active workspace.",
|
||||||
"noDueDate": "No due date",
|
"noDueDate": "No due date",
|
||||||
|
|||||||
@@ -42,24 +42,20 @@
|
|||||||
"createAction": "Ajouter un espace",
|
"createAction": "Ajouter un espace",
|
||||||
"organizationLabel": "Organisation",
|
"organizationLabel": "Organisation",
|
||||||
"organizationSettings": "Parametres de l'organisation",
|
"organizationSettings": "Parametres de l'organisation",
|
||||||
"noOrganization": "Aucune organisation"
|
"noOrganization": "Aucune organisation",
|
||||||
|
"workspaceSettings": "Parametres de l'espace"
|
||||||
},
|
},
|
||||||
"workspaceCreate": {
|
"workspaceCreate": {
|
||||||
"eyebrow": "Espace",
|
"eyebrow": "Espace",
|
||||||
"title": "Creer un nouvel espace",
|
"title": "Creer un nouvel espace",
|
||||||
"description": "Configurez un nouvel espace avec son slug, son fuseau horaire, ses membres, son workflow et ses connecteurs.",
|
"description": "Configurez un nouvel espace avec son fuseau horaire, ses membres, son workflow et ses connecteurs.",
|
||||||
"previewTitle": "URL de l'espace",
|
|
||||||
"previewDescription": "Le slug devient l'identifiant stable utilise pour l'espace.",
|
|
||||||
"formTitle": "Details de l'espace",
|
"formTitle": "Details de l'espace",
|
||||||
"formDescription": "Commencez par les champs essentiels. Les membres, le workflow et les connecteurs peuvent etre configures juste apres la creation.",
|
"formDescription": "Commencez par les champs essentiels. Les membres, le workflow et les connecteurs peuvent etre configures juste apres la creation.",
|
||||||
"createAction": "Creer l'espace",
|
"createAction": "Creer l'espace",
|
||||||
"slugHint": "Apercu du slug : {slug}",
|
|
||||||
"fields": {
|
"fields": {
|
||||||
"name": "Nom de l'espace",
|
"name": "Nom de l'espace",
|
||||||
"namePlaceholder": "Northwind Studio",
|
"namePlaceholder": "Northwind Studio",
|
||||||
"organization": "Organisation",
|
"organization": "Organisation",
|
||||||
"slug": "Slug de l'espace",
|
|
||||||
"slugPlaceholder": "northwind-studio",
|
|
||||||
"timeZone": "Fuseau horaire"
|
"timeZone": "Fuseau horaire"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
@@ -131,7 +127,7 @@
|
|||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
"dashboard": "Tableau de bord",
|
"dashboard": "Tableau de bord",
|
||||||
"overview": "Vue globale",
|
"overview": "Vue globale",
|
||||||
"workspacePlan": "Contenu",
|
"workspacePlan": "Calendrier",
|
||||||
"mediaLibrary": "Bibliotheque media",
|
"mediaLibrary": "Bibliotheque media",
|
||||||
"myFeedback": "Mon feedback",
|
"myFeedback": "Mon feedback",
|
||||||
"feedbackReview": "Revue feedback",
|
"feedbackReview": "Revue feedback",
|
||||||
@@ -493,6 +489,7 @@
|
|||||||
"description": "Unités révisables avec ressources, texte et statut d'approbation dans l'espace actif.",
|
"description": "Unités révisables avec ressources, texte et statut d'approbation dans l'espace actif.",
|
||||||
"newItem": "Nouvel élément de contenu",
|
"newItem": "Nouvel élément de contenu",
|
||||||
"createTitle": "Créer un élément de contenu",
|
"createTitle": "Créer un élément de contenu",
|
||||||
|
"upcoming": "À venir",
|
||||||
"loading": "Chargement des éléments de contenu...",
|
"loading": "Chargement des éléments de contenu...",
|
||||||
"empty": "Aucun élément de contenu n'est disponible pour l'espace actif.",
|
"empty": "Aucun élément de contenu n'est disponible pour l'espace actif.",
|
||||||
"noDueDate": "Aucune échéance",
|
"noDueDate": "Aucune échéance",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const ForgotPasswordView = () => import('@/features/auth/views/ForgotPasswordVie
|
|||||||
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
|
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
|
||||||
const VerifyEmailView = () => import('@/features/auth/views/VerifyEmailView.vue');
|
const VerifyEmailView = () => import('@/features/auth/views/VerifyEmailView.vue');
|
||||||
const OverviewView = () => import('@/features/workspaces/views/OverviewView.vue');
|
const OverviewView = () => import('@/features/workspaces/views/OverviewView.vue');
|
||||||
const DashboardView = () => import('@/features/workspaces/views/DashboardView.vue');
|
|
||||||
const ChannelsView = () => import('@/features/channels/views/ChannelsView.vue');
|
const ChannelsView = () => import('@/features/channels/views/ChannelsView.vue');
|
||||||
const CampaignsView = () => import('@/features/campaigns/views/CampaignsView.vue');
|
const CampaignsView = () => import('@/features/campaigns/views/CampaignsView.vue');
|
||||||
const CampaignDetailView = () => import('@/features/campaigns/views/CampaignDetailView.vue');
|
const CampaignDetailView = () => import('@/features/campaigns/views/CampaignDetailView.vue');
|
||||||
@@ -70,7 +69,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/app/workspace',
|
path: '/app/workspace',
|
||||||
name: 'workspace-dashboard',
|
name: 'workspace-dashboard',
|
||||||
component: DashboardView,
|
redirect: { name: 'content-items' },
|
||||||
meta: { requiresAuth: true },
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"url": "http://localhost:5080"
|
"url": "http://localhost:5081"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
@@ -2961,9 +2961,6 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"slug": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"logoUrl": {
|
"logoUrl": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -3036,7 +3033,6 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"organizationId",
|
"organizationId",
|
||||||
"name",
|
"name",
|
||||||
"slug",
|
|
||||||
"timeZone"
|
"timeZone"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -3052,13 +3048,6 @@
|
|||||||
"minLength": 0,
|
"minLength": 0,
|
||||||
"nullable": false
|
"nullable": false
|
||||||
},
|
},
|
||||||
"slug": {
|
|
||||||
"type": "string",
|
|
||||||
"maxLength": 128,
|
|
||||||
"minLength": 0,
|
|
||||||
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
||||||
"nullable": false
|
|
||||||
},
|
|
||||||
"timeZone": {
|
"timeZone": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"maxLength": 128,
|
"maxLength": 128,
|
||||||
|
|||||||
Reference in New Issue
Block a user