fix: seed all plans/features in prod (upsert); fix admin toggle RTL knob
CI/CD / CI · API (dotnet build + test) (push) Successful in 50s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 39s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Failing after 5m44s

Plan + feature seeding was dev-gated and all-or-nothing, so production only had the Free plan (admin Plans page showed one). Now runs in every environment and upserts missing rows (adds Pro/Business/Enterprise on top of the existing Free). Also force LTR on the admin toggle switch so the knob doesn't render off-track under the RTL page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 00:23:17 +03:30
parent 75d5bbc84a
commit a83edf7667
2 changed files with 21 additions and 12 deletions
@@ -33,6 +33,12 @@ public static class PlatformDataSeeder
// from their city, so the public map lights up. Idempotent (fills nulls). // from their city, so the public map lights up. Idempotent (fills nulls).
await BackfillCafeLocationsAsync(db, logger); await BackfillCafeLocationsAsync(db, logger);
// Subscription plans + feature flags are platform config the admin panel
// reads in every environment. Idempotent: adds any rows that are missing
// (so prod, which only had the Free plan, gets Pro/Business/Enterprise).
await SeedPlansAsync(db, logger);
await SeedFeaturesAsync(db, logger);
if (!env.IsDevelopment()) if (!env.IsDevelopment())
{ {
// Production: also ensure integration settings (Kavenegar enabled/template, // Production: also ensure integration settings (Kavenegar enabled/template,
@@ -43,8 +49,6 @@ public static class PlatformDataSeeder
await EnsureCatalogUpgradesAsync(db, logger); await EnsureCatalogUpgradesAsync(db, logger);
await SeedSystemAdminAsync(db, logger); await SeedSystemAdminAsync(db, logger);
await SeedPlansAsync(db, logger);
await SeedFeaturesAsync(db, logger);
await SeedSettingsAsync(db, logger); await SeedSettingsAsync(db, logger);
await EnsureIntegrationSettingsAsync(db, logger); await EnsureIntegrationSettingsAsync(db, logger);
} }
@@ -357,9 +361,6 @@ public static class PlatformDataSeeder
private static async Task SeedPlansAsync(AppDbContext db, ILogger logger) private static async Task SeedPlansAsync(AppDbContext db, ILogger logger)
{ {
if (await db.PlatformPlanDefinitions.AnyAsync())
return;
var plans = new[] var plans = new[]
{ {
new PlatformPlanDefinition new PlatformPlanDefinition
@@ -421,16 +422,18 @@ public static class PlatformDataSeeder
} }
}; };
db.PlatformPlanDefinitions.AddRange(plans); var existingIds = (await db.PlatformPlanDefinitions.Select(p => p.Id).ToListAsync())
.ToHashSet(StringComparer.Ordinal);
var missing = plans.Where(p => !existingIds.Contains(p.Id)).ToArray();
if (missing.Length == 0) return;
db.PlatformPlanDefinitions.AddRange(missing);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
logger.LogInformation("Platform seed: {Count} subscription plans", plans.Length); logger.LogInformation("Platform seed: +{Count} subscription plans", missing.Length);
} }
private static async Task SeedFeaturesAsync(AppDbContext db, ILogger logger) private static async Task SeedFeaturesAsync(AppDbContext db, ILogger logger)
{ {
if (await db.PlatformFeatures.AnyAsync())
return;
var features = new[] var features = new[]
{ {
F("pos", "صندوق", "POS", "core"), F("pos", "صندوق", "POS", "core"),
@@ -456,9 +459,14 @@ public static class PlatformDataSeeder
F("discover_profile", "پروفایل کشف", "Discover profile", "growth") F("discover_profile", "پروفایل کشف", "Discover profile", "growth")
}; };
db.PlatformFeatures.AddRange(features); var existingIds = (await db.PlatformFeatures.Select(f => f.Id).ToListAsync())
.ToHashSet(StringComparer.Ordinal);
var missing = features.Where(f => !existingIds.Contains(f.Id)).ToArray();
if (missing.Length == 0) return;
db.PlatformFeatures.AddRange(missing);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
logger.LogInformation("Platform seed: {Count} feature flags", features.Length); logger.LogInformation("Platform seed: +{Count} feature flags", missing.Length);
} }
private static PlatformFeature F(string key, string fa, string en, string group) => new() private static PlatformFeature F(string key, string fa, string en, string group) => new()
@@ -44,6 +44,7 @@ function Toggle({ checked, onChange, disabled }: { checked: boolean; onChange: (
<button <button
type="button" type="button"
role="switch" role="switch"
dir="ltr"
aria-checked={checked} aria-checked={checked}
disabled={disabled} disabled={disabled}
onClick={() => onChange(!checked)} onClick={() => onChange(!checked)}