Update new version checking and Report Categories
This commit is contained in:
parent
1d05d24e41
commit
28ec641275
13 changed files with 413 additions and 80 deletions
|
|
@ -19949,6 +19949,72 @@
|
||||||
"key": "App.Tenant.Create",
|
"key": "App.Tenant.Create",
|
||||||
"en": "Tenant Create",
|
"en": "Tenant Create",
|
||||||
"tr": "Tenant Oluştur"
|
"tr": "Tenant Oluştur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.CheckUpdate",
|
||||||
|
"en": "Checking...",
|
||||||
|
"tr": "Kontrol ediliyor..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.CheckForUpdates",
|
||||||
|
"en": "Check for Updates",
|
||||||
|
"tr": "Güncellemeyi Kontrol Et"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.NotSupportServiceWorkerUpdate",
|
||||||
|
"en": "Your browser does not support service worker updates.",
|
||||||
|
"tr": "Tarayıcınız service worker güncellemelerini desteklemiyor."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.NewVersionAvailable",
|
||||||
|
"en": "A new service worker has been found. The application will automatically update when the files are ready.",
|
||||||
|
"tr": "Dosyalar hazırlandığında uygulama otomatik olarak güncellenecek."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.ManualUpdateFailed",
|
||||||
|
"en": "Please check your connection and try again.",
|
||||||
|
"tr": "Lütfen bağlantınızı kontrol edip tekrar deneyin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.ChangeLogDescription",
|
||||||
|
"en": "Check if there's a new application version available on the server.",
|
||||||
|
"tr": "Sunucuda yeni bir uygulama sürümü olup olmadığını kontrol edin."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.NoNewVersionFound",
|
||||||
|
"en": "No new version was found available.",
|
||||||
|
"tr": "Kullanılabilir yeni bir sürüm bulunamadı."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.UpToDate",
|
||||||
|
"en": "The app is up to date.",
|
||||||
|
"tr": "Uygulama güncel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.UpdateCouldNotBeChecked",
|
||||||
|
"en": "Update could not be checked",
|
||||||
|
"tr": "Güncelleme kontrol edilemedi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.NewVersionFound",
|
||||||
|
"en": "New version found.",
|
||||||
|
"tr": "Yeni sürüm bulundu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.UpdateCheckFailed",
|
||||||
|
"en": "Update check failed.",
|
||||||
|
"tr": "Güncelleme kontrolü başarısız"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1417,7 +1417,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ReportCategory)),
|
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.ReportCategory)),
|
||||||
KeyFieldName = "Id",
|
KeyFieldName = "Id",
|
||||||
DefaultFilter = DefaultFilterJson,
|
DefaultFilter = DefaultFilterJson,
|
||||||
KeyFieldDbSourceType = DbType.Int32,
|
KeyFieldDbSourceType = DbType.Guid,
|
||||||
SortMode = GridOptions.SortModeSingle,
|
SortMode = GridOptions.SortModeSingle,
|
||||||
FilterRowJson = DefaultFilterRowJson(),
|
FilterRowJson = DefaultFilterRowJson(),
|
||||||
HeaderFilterJson = DefaultHeaderFilterJson(),
|
HeaderFilterJson = DefaultHeaderFilterJson(),
|
||||||
|
|
@ -1439,8 +1439,8 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
]}
|
]}
|
||||||
}),
|
}),
|
||||||
DeleteCommand = $"UPDATE \"{FullNameTable(TableNameEnum.ReportCategory)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
DeleteCommand = $"UPDATE \"{FullNameTable(TableNameEnum.ReportCategory)}\" SET \"DeleterId\"=@DeleterId, \"DeletionTime\"=CURRENT_TIMESTAMP, \"IsDeleted\"='true' WHERE \"Id\"=@Id",
|
||||||
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(DbType.String),
|
DeleteFieldsDefaultValueJson = DefaultDeleteFieldsDefaultValueJson(),
|
||||||
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(DbType.String, "Name"),
|
InsertFieldsDefaultValueJson = DefaultInsertFieldsDefaultValueJson(),
|
||||||
});
|
});
|
||||||
|
|
||||||
#region Report Categories Fields
|
#region Report Categories Fields
|
||||||
|
|
@ -1582,7 +1582,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
AuthName = listFormName,
|
AuthName = listFormName,
|
||||||
Url=$"/admin/reports/DynamicReport/view/@Id",
|
Url=$"/admin/reports/DynamicReport/view/@Id",
|
||||||
IsVisible = true,
|
IsVisible = true,
|
||||||
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
|
VisibleExpression = "((e)=> e.component.columnOption('CategoryId').lookup.calculateCellValue(e.row.data.CategoryId) === 'Raporlar')"
|
||||||
},
|
},
|
||||||
new() {
|
new() {
|
||||||
Hint = "App.Platform.Design",
|
Hint = "App.Platform.Design",
|
||||||
|
|
@ -1591,7 +1591,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
AuthName = listFormName + ".Update",
|
AuthName = listFormName + ".Update",
|
||||||
Url=$"/admin/reports/DynamicReport/design/@Id",
|
Url=$"/admin/reports/DynamicReport/design/@Id",
|
||||||
IsVisible = true,
|
IsVisible = true,
|
||||||
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Raporlar')"
|
VisibleExpression = "((e)=> e.component.columnOption('CategoryId').lookup.calculateCellValue(e.row.data.CategoryId) === 'Raporlar')"
|
||||||
},
|
},
|
||||||
new() {
|
new() {
|
||||||
Hint = "App.Platform.View",
|
Hint = "App.Platform.View",
|
||||||
|
|
@ -1600,7 +1600,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
AuthName = listFormName,
|
AuthName = listFormName,
|
||||||
Url=$"/admin/reports/DynamicGrid/view/@Name",
|
Url=$"/admin/reports/DynamicGrid/view/@Name",
|
||||||
IsVisible = true,
|
IsVisible = true,
|
||||||
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Listeler')"
|
VisibleExpression = "((e)=> e.component.columnOption('CategoryId').lookup.calculateCellValue(e.row.data.CategoryId) === 'Listeler')"
|
||||||
},
|
},
|
||||||
new() {
|
new() {
|
||||||
Hint = "App.Platform.View",
|
Hint = "App.Platform.View",
|
||||||
|
|
@ -1609,7 +1609,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
AuthName = listFormName,
|
AuthName = listFormName,
|
||||||
Url=$"/admin/reports/DynamicGrid/view/@Id/@Name",
|
Url=$"/admin/reports/DynamicGrid/view/@Id/@Name",
|
||||||
IsVisible = true,
|
IsVisible = true,
|
||||||
VisibleExpression = "((e)=> e.row.data.CategoryId === 'Formlar')"
|
VisibleExpression = "((e)=> e.component.columnOption('CategoryId').lookup.calculateCellValue(e.row.data.CategoryId) === 'Formlar')"
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
@ -1660,7 +1660,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
|
||||||
new() {
|
new() {
|
||||||
ListFormCode = listForm.ListFormCode,
|
ListFormCode = listForm.ListFormCode,
|
||||||
CultureName = LanguageCodes.En,
|
CultureName = LanguageCodes.En,
|
||||||
SourceDbType = DbType.String,
|
SourceDbType = DbType.Guid,
|
||||||
FieldName = "CategoryId",
|
FieldName = "CategoryId",
|
||||||
CaptionName = "App.Listform.ListformField.CategoryId",
|
CaptionName = "App.Listform.ListformField.CategoryId",
|
||||||
Width = 0,
|
Width = 0,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Entities
|
namespace Sozsoft.Platform.Entities
|
||||||
{
|
{
|
||||||
public class ReportCategory : FullAuditedEntity<string>, IMultiTenant
|
public class ReportCategory : FullAuditedEntity<Guid>, IMultiTenant
|
||||||
{
|
{
|
||||||
public Guid? TenantId { get; set; }
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
|
|
@ -15,9 +15,12 @@ namespace Sozsoft.Platform.Entities
|
||||||
|
|
||||||
public ICollection<ReportTemplate> ReportTemplates { get; set; }
|
public ICollection<ReportTemplate> ReportTemplates { get; set; }
|
||||||
|
|
||||||
public ReportCategory(string id)
|
protected ReportCategory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReportCategory(Guid id) : base(id)
|
||||||
{
|
{
|
||||||
Id = id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Sozsoft.Platform.Entities
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string HtmlContent { get; set; }
|
public string HtmlContent { get; set; }
|
||||||
|
|
||||||
public string CategoryId { get; set; }
|
public Guid CategoryId { get; set; }
|
||||||
public ReportCategory ReportCategory { get; set; }
|
public ReportCategory ReportCategory { get; set; }
|
||||||
|
|
||||||
public string Status { get; set; }
|
public string Status { get; set; }
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Sozsoft.Platform.Entities
|
||||||
string name,
|
string name,
|
||||||
string description,
|
string description,
|
||||||
string htmlContent,
|
string htmlContent,
|
||||||
string categoryId,
|
Guid categoryId,
|
||||||
string status
|
string status
|
||||||
) : base(id)
|
) : base(id)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -660,7 +660,6 @@ public class PlatformDbContext :
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ReportCategory)), Prefix.DbSchema);
|
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ReportCategory)), Prefix.DbSchema);
|
||||||
b.ConfigureByConvention();
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
b.Property(x => x.Id).HasMaxLength(256);
|
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.Description).HasMaxLength(4096); // JSON string
|
b.Property(x => x.Description).HasMaxLength(4096); // JSON string
|
||||||
b.Property(x => x.Icon).HasMaxLength(64);
|
b.Property(x => x.Icon).HasMaxLength(64);
|
||||||
|
|
@ -674,7 +673,6 @@ public class PlatformDbContext :
|
||||||
b.ConfigureByConvention();
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.CategoryId).HasMaxLength(256);
|
|
||||||
b.Property(x => x.Description).HasMaxLength(1024);
|
b.Property(x => x.Description).HasMaxLength(1024);
|
||||||
b.Property(x => x.HtmlContent).IsRequired();
|
b.Property(x => x.HtmlContent).IsRequired();
|
||||||
b.Property(x => x.Status).HasMaxLength(20);
|
b.Property(x => x.Status).HasMaxLength(20);
|
||||||
|
|
|
||||||
|
|
@ -4482,9 +4482,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<Guid>("Id")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uniqueidentifier");
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -4547,9 +4546,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
b.Property<string>("CategoryId")
|
b.Property<Guid>("CategoryId")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uniqueidentifier");
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
|
||||||
|
|
@ -650,7 +650,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
name: "Adm_T_ReportCategory",
|
name: "Adm_T_ReportCategory",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Description = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
|
Description = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
|
||||||
|
|
@ -2259,7 +2259,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
|
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
|
||||||
HtmlContent = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
HtmlContent = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
CategoryId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
CategoryId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
Status = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
Status = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
|
|
||||||
|
|
@ -4479,9 +4479,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b =>
|
modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<Guid>("Id")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uniqueidentifier");
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -4544,9 +4543,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
b.Property<string>("CategoryId")
|
b.Property<Guid>("CategoryId")
|
||||||
.HasMaxLength(256)
|
.HasColumnType("uniqueidentifier");
|
||||||
.HasColumnType("nvarchar(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
|
||||||
|
|
@ -491,7 +491,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
private readonly IRepository<GlobalSearch, int> _globalSearch;
|
private readonly IRepository<GlobalSearch, int> _globalSearch;
|
||||||
private readonly IRepository<CustomEndpoint, Guid> _customEndpointRepository;
|
private readonly IRepository<CustomEndpoint, Guid> _customEndpointRepository;
|
||||||
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
private readonly IRepository<CustomComponent, Guid> _customComponentRepository;
|
||||||
private readonly IRepository<ReportCategory, string> _reportCategoriesRepository;
|
private readonly IRepository<ReportCategory, Guid> _reportCategoriesRepository;
|
||||||
private readonly IRepository<ReportTemplate, Guid> _reportTemplatesRepository;
|
private readonly IRepository<ReportTemplate, Guid> _reportTemplatesRepository;
|
||||||
private readonly IRepository<Home, Guid> _homeRepository;
|
private readonly IRepository<Home, Guid> _homeRepository;
|
||||||
private readonly IRepository<About, Guid> _aboutRepository;
|
private readonly IRepository<About, Guid> _aboutRepository;
|
||||||
|
|
@ -548,7 +548,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
IRepository<SkillLevel, string> skillLevelRepository,
|
IRepository<SkillLevel, string> skillLevelRepository,
|
||||||
IRepository<CustomEndpoint, Guid> customEndpointRepository,
|
IRepository<CustomEndpoint, Guid> customEndpointRepository,
|
||||||
IRepository<CustomComponent, Guid> customComponentRepository,
|
IRepository<CustomComponent, Guid> customComponentRepository,
|
||||||
IRepository<ReportCategory, string> reportCategoriesRepository,
|
IRepository<ReportCategory, Guid> reportCategoriesRepository,
|
||||||
IRepository<ReportTemplate, Guid> reportTemplatesRepository,
|
IRepository<ReportTemplate, Guid> reportTemplatesRepository,
|
||||||
IRepository<Home, Guid> homeRepository,
|
IRepository<Home, Guid> homeRepository,
|
||||||
IRepository<About, Guid> aboutRepository,
|
IRepository<About, Guid> aboutRepository,
|
||||||
|
|
@ -867,7 +867,7 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
|
||||||
|
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
await _reportCategoriesRepository.InsertAsync(new ReportCategory(item.Name)
|
await _reportCategoriesRepository.InsertAsync(new ReportCategory(Guid.NewGuid())
|
||||||
{
|
{
|
||||||
Name = item.Name,
|
Name = item.Name,
|
||||||
Description = item.Description,
|
Description = item.Description,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
{
|
{
|
||||||
"commit": "e7be51a",
|
"commit": "1d05d24",
|
||||||
"releases": [
|
"releases": [
|
||||||
|
{
|
||||||
|
"version": "1.1.07",
|
||||||
|
"buildDate": "2026-07-13",
|
||||||
|
"commit": "50cb3eaf979eb5027172a92a4e941ed64aa3671e",
|
||||||
|
"changeLog": [
|
||||||
|
"- localStorage tüm bilgiler easy-peasy içerisinde birleştirildi.",
|
||||||
|
"- Codex 5.6 sol optimizasyon yapıldı. Bir çok komponent Lazy load ile yüklendi.",
|
||||||
|
"- Workflow komponentinden birden fazla kişi onay verebilir.",
|
||||||
|
"- Posta Kodu eklendi.",
|
||||||
|
"- Sales Order kısmında Yeni ve Eski müşteri sipariş ayrımı yapıldı"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.1.06",
|
"version": "1.1.06",
|
||||||
"buildDate": "2026-07-06",
|
"buildDate": "2026-07-06",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import React, { useEffect, useState } from "react"
|
import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||||
import AdaptableCard from "@/components/shared/AdaptableCard"
|
import Container from '@/components/shared/Container'
|
||||||
import Container from "@/components/shared/Container"
|
import { Button, Notification, toast } from '@/components/ui'
|
||||||
import { FaTag, FaRegCheckCircle } from "react-icons/fa"
|
import { APP_NAME } from '@/constants/app.constant'
|
||||||
import { useLocalization } from "@/utils/hooks/useLocalization"
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Helmet } from "react-helmet"
|
import React, { useEffect, useState } from 'react'
|
||||||
import { APP_NAME } from "@/constants/app.constant"
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { FaRegCheckCircle, FaSyncAlt, FaTag } from 'react-icons/fa'
|
||||||
|
import { checkForAppUpdate } from './swRegistration'
|
||||||
|
|
||||||
type Release = {
|
type Release = {
|
||||||
version: string
|
version: string
|
||||||
|
|
@ -12,7 +14,15 @@ type Release = {
|
||||||
changeLog: string[]
|
changeLog: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const Log = ({ version, date, children }: { version: string; date: string; children?: React.ReactNode }) => {
|
const Log = ({
|
||||||
|
version,
|
||||||
|
date,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
version: string
|
||||||
|
date: string
|
||||||
|
children?: React.ReactNode
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="relative pl-4 sm:pl-32 py-4 group">
|
<div className="relative pl-4 sm:pl-32 py-4 group">
|
||||||
<div className="flex flex-col sm:flex-row items-start mb-1 group-last:before:hidden before:absolute before:left-2 sm:before:left-0 before:h-full before:px-px before:bg-slate-200 sm:before:ml-[6.5rem] before:self-start before:-translate-x-1/2 before:translate-y-3 after:absolute after:left-2 sm:after:left-0 after:w-2 after:h-2 after:bg-indigo-600 after:border-4 after:box-content after:border-slate-50 after:rounded-full sm:after:ml-[6.5rem] after:-translate-x-1/2 after:translate-y-1.5">
|
<div className="flex flex-col sm:flex-row items-start mb-1 group-last:before:hidden before:absolute before:left-2 sm:before:left-0 before:h-full before:px-px before:bg-slate-200 sm:before:ml-[6.5rem] before:self-start before:-translate-x-1/2 before:translate-y-3 after:absolute after:left-2 sm:after:left-0 after:w-2 after:h-2 after:bg-indigo-600 after:border-4 after:box-content after:border-slate-50 after:rounded-full sm:after:ml-[6.5rem] after:-translate-x-1/2 after:translate-y-1.5">
|
||||||
|
|
@ -20,8 +30,7 @@ const Log = ({ version, date, children }: { version: string; date: string; child
|
||||||
{date}
|
{date}
|
||||||
</time>
|
</time>
|
||||||
<div className="flex items-center text-xl font-bold text-gray-900 dark:text-white">
|
<div className="flex items-center text-xl font-bold text-gray-900 dark:text-white">
|
||||||
<FaTag className="mr-2 text-indigo-500" />
|
<FaTag className="mr-2 text-indigo-500" />v{version}
|
||||||
v{version}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-500">{children}</div>
|
<div className="text-slate-500">{children}</div>
|
||||||
|
|
@ -32,9 +41,10 @@ const Log = ({ version, date, children }: { version: string; date: string; child
|
||||||
const Changelog = () => {
|
const Changelog = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const [releases, setReleases] = useState<Release[]>([])
|
const [releases, setReleases] = useState<Release[]>([])
|
||||||
|
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/version.json?ts=" + Date.now())
|
fetch('/version.json?ts=' + Date.now())
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data?.releases) {
|
if (data?.releases) {
|
||||||
|
|
@ -44,15 +54,73 @@ const Changelog = () => {
|
||||||
.catch(() => setReleases([]))
|
.catch(() => setReleases([]))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const handleManualUpdate = async () => {
|
||||||
|
if (isCheckingUpdate) return
|
||||||
|
setIsCheckingUpdate(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await checkForAppUpdate()
|
||||||
|
|
||||||
|
if (result === 'up-to-date') {
|
||||||
|
toast.push(
|
||||||
|
<Notification title={translate('::App.UpToDate')} type="success">
|
||||||
|
{translate('::App.NoNewVersionFound')}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'bottom-end' },
|
||||||
|
)
|
||||||
|
} else if (result === 'unsupported') {
|
||||||
|
toast.push(
|
||||||
|
<Notification title={translate('::App.UpdateCouldNotBeChecked')} type="warning">
|
||||||
|
{translate('::App.NotSupportServiceWorkerUpdate')}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'bottom-end' },
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toast.push(
|
||||||
|
<Notification title={translate('::App.NewVersionFound')} type="info">
|
||||||
|
{translate('::App.NewVersionAvailable')}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'bottom-end' },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Manual application update failed.', error)
|
||||||
|
toast.push(
|
||||||
|
<Notification title={translate('::App.UpdateCheckFailed')} type="danger">
|
||||||
|
{translate('::App.ManualUpdateFailed')}
|
||||||
|
</Notification>,
|
||||||
|
{ placement: 'bottom-end' },
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
setIsCheckingUpdate(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Helmet
|
<Helmet
|
||||||
titleTemplate={`%s | ${APP_NAME}`}
|
titleTemplate={`%s | ${APP_NAME}`}
|
||||||
title={translate("::App.ChangeLog")}
|
title={translate('::App.ChangeLog')}
|
||||||
defaultTitle={APP_NAME}
|
defaultTitle={APP_NAME}
|
||||||
/>
|
/>
|
||||||
<AdaptableCard>
|
<AdaptableCard>
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
|
<div className="mb-6 flex flex-col gap-3 border-b border-gray-200 pb-5 dark:border-gray-700 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className="mb-1">{translate('::App.ChangeLog')}</h3>
|
||||||
|
<p className="text-sm text-gray-500">{translate('::App.ChangeLogDescription')}</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
icon={<FaSyncAlt />}
|
||||||
|
loading={isCheckingUpdate}
|
||||||
|
variant="solid"
|
||||||
|
onClick={handleManualUpdate}
|
||||||
|
>
|
||||||
|
{isCheckingUpdate
|
||||||
|
? translate('::App.CheckUpdate')
|
||||||
|
: translate('::App.CheckForUpdates')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{releases.map((rel) => (
|
{releases.map((rel) => (
|
||||||
<Log key={rel.version} version={rel.version} date={rel.buildDate}>
|
<Log key={rel.version} version={rel.version} date={rel.buildDate}>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
import { registerSW } from 'virtual:pwa-register'
|
import { registerSW } from 'virtual:pwa-register'
|
||||||
|
|
||||||
|
const ACTIVATION_RETRY_DELAY = 5_000
|
||||||
|
const ACTIVATION_TIMEOUT = 30_000
|
||||||
|
|
||||||
|
let registrationStarted = false
|
||||||
|
let activationInProgress = false
|
||||||
|
let reloadStarted = false
|
||||||
|
let currentRegistration: ServiceWorkerRegistration | undefined
|
||||||
|
let activateUpdateHandler: ((reloadPage?: boolean) => Promise<void>) | undefined
|
||||||
|
let registrationInitialization: Promise<void> | undefined
|
||||||
|
let activationRetryTimer: number | undefined
|
||||||
|
let activationTimeoutTimer: number | undefined
|
||||||
|
const watchedInstallingWorkers = new WeakSet<ServiceWorker>()
|
||||||
|
|
||||||
|
export type ManualUpdateResult = 'up-to-date' | 'updating' | 'unsupported'
|
||||||
|
|
||||||
function showUpdateOverlay() {
|
function showUpdateOverlay() {
|
||||||
if (document.getElementById('sw-update-overlay')) return
|
if (document.getElementById('sw-update-overlay')) return
|
||||||
|
|
||||||
|
|
@ -67,30 +82,202 @@ function showUpdateOverlay() {
|
||||||
document.body.appendChild(overlay)
|
document.body.appendChild(overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerServiceWorker = () => {
|
function hideUpdateOverlay() {
|
||||||
// autoUpdate modunda onNeedRefresh tetiklenmez.
|
document.getElementById('sw-update-overlay')?.remove()
|
||||||
// updatefound → yeni SW installing → overlay aç.
|
document.getElementById('sw-update-overlay-style')?.remove()
|
||||||
// skipWaiting: true (workbox config) → controllerchange → autoUpdate reload.
|
}
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready.then((registration) => {
|
function clearActivationTimers() {
|
||||||
// ready'den önce updatefound zaten tetiklendiyse (race condition)
|
window.clearTimeout(activationRetryTimer)
|
||||||
if (registration.installing && navigator.serviceWorker.controller) {
|
window.clearTimeout(activationTimeoutTimer)
|
||||||
|
activationRetryTimer = undefined
|
||||||
|
activationTimeoutTimer = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadForUpdate() {
|
||||||
|
if (reloadStarted) return
|
||||||
|
reloadStarted = true
|
||||||
|
clearActivationTimers()
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateWaitingWorker(
|
||||||
|
registration: ServiceWorkerRegistration | undefined,
|
||||||
|
activateUpdate: (reloadPage?: boolean) => Promise<void>,
|
||||||
|
showOverlay: boolean,
|
||||||
|
) {
|
||||||
|
if (activationInProgress) {
|
||||||
|
if (showOverlay) showUpdateOverlay()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activationInProgress = true
|
||||||
|
|
||||||
|
if (showOverlay) {
|
||||||
showUpdateOverlay()
|
showUpdateOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workbox'ın controlling olayı herhangi bir nedenle kaçarsa native olay da
|
||||||
|
// yeni worker kontrolü aldığı anda sayfayı yeniler.
|
||||||
|
navigator.serviceWorker.addEventListener('controllerchange', reloadForUpdate, {
|
||||||
|
once: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Workbox callback sırasına bağlı kalmadan waiting worker'ı doğrudan geçir.
|
||||||
|
registration?.waiting?.postMessage({ type: 'SKIP_WAITING' })
|
||||||
|
|
||||||
|
void activateUpdate(true).catch((error: unknown) => {
|
||||||
|
console.error('Service worker activation failed.', error)
|
||||||
|
activationInProgress = false
|
||||||
|
clearActivationTimers()
|
||||||
|
hideUpdateOverlay()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bazı tarayıcılarda Workbox mesajı bekleyen worker'a ulaşmayabiliyor.
|
||||||
|
// Kısa bir süre sonra native mesajla bir kez daha aktivasyon istenir.
|
||||||
|
activationRetryTimer = window.setTimeout(() => {
|
||||||
|
registration?.waiting?.postMessage({ type: 'SKIP_WAITING' })
|
||||||
|
}, ACTIVATION_RETRY_DELAY)
|
||||||
|
|
||||||
|
// Aktivasyon yine de tamamlanamazsa uygulamayı sonsuza kadar kilitleme.
|
||||||
|
// Eski worker çalışmaya devam eder; bir sonraki açılışta tekrar denenir.
|
||||||
|
activationTimeoutTimer = window.setTimeout(() => {
|
||||||
|
activationInProgress = false
|
||||||
|
clearActivationTimers()
|
||||||
|
|
||||||
|
// Worker waiting durumundan çıktıysa aktivasyon gerçekleşmiş fakat
|
||||||
|
// controllerchange olayı kaçmış olabilir. Yeni precache'i almak için yenile.
|
||||||
|
if (!registration?.waiting && registration?.active) {
|
||||||
|
reloadForUpdate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hideUpdateOverlay()
|
||||||
|
console.warn('Service worker activation timed out; continuing with current version.')
|
||||||
|
}, ACTIVATION_TIMEOUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
function showUpdateWhileInstalling(registration: ServiceWorkerRegistration) {
|
||||||
|
const installingWorker = registration.installing
|
||||||
|
if (
|
||||||
|
!installingWorker ||
|
||||||
|
!navigator.serviceWorker.controller ||
|
||||||
|
watchedInstallingWorkers.has(installingWorker)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
watchedInstallingWorkers.add(installingWorker)
|
||||||
|
showUpdateOverlay()
|
||||||
|
|
||||||
|
installingWorker.addEventListener('statechange', () => {
|
||||||
|
// Precache dosyalarından biri indirilemezse worker redundant olur. Eski
|
||||||
|
// uygulama çalışmaya devam eder ve kullanıcı kilitli ekranda bırakılmaz.
|
||||||
|
if (installingWorker.state === 'redundant' && !activationInProgress) {
|
||||||
|
hideUpdateOverlay()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchForUpdate(registration: ServiceWorkerRegistration) {
|
||||||
registration.addEventListener('updatefound', () => {
|
registration.addEventListener('updatefound', () => {
|
||||||
// Güncelleme başlar başlamaz overlay'i göster
|
// Gerçek worker güncellemesi bulunduğu anda, precache indirmesi başlamadan
|
||||||
if (navigator.serviceWorker.controller) {
|
// Loading'i göster ve aktivasyon/reload tamamlanana kadar açık tut.
|
||||||
showUpdateOverlay()
|
showUpdateWhileInstalling(registration)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Listener bağlanmadan hemen önce başlamış bir kurulumu da kaçırma.
|
||||||
|
showUpdateWhileInstalling(registration)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForUpdate(registration: ServiceWorkerRegistration) {
|
||||||
|
// Bir deploy versiyon numarası/tag değişmeden yapılsa bile sw.js içindeki
|
||||||
|
// precache manifest'i değişir. Uygulama açılırken yapılan bu tek kontrol
|
||||||
|
// yeni build'i algılar; açık uygulamada periyodik bir arka plan işi çalışmaz.
|
||||||
|
void registration.update().catch((error: unknown) => {
|
||||||
|
console.warn('Service worker update check failed.', error)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerServiceWorker = () => {
|
||||||
|
if (registrationStarted || !('serviceWorker' in navigator)) return
|
||||||
|
registrationStarted = true
|
||||||
|
|
||||||
|
registrationInitialization = navigator.serviceWorker
|
||||||
|
.getRegistration()
|
||||||
|
.catch(() => undefined)
|
||||||
|
.then((existingRegistration) => {
|
||||||
|
if (existingRegistration) {
|
||||||
|
watchForUpdate(existingRegistration)
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSW({
|
const activateUpdate = registerSW({
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
onRegisteredSW(_swUrl, registration) {
|
||||||
|
if (!registration) return
|
||||||
|
|
||||||
|
if (registration !== existingRegistration) {
|
||||||
|
watchForUpdate(registration)
|
||||||
|
}
|
||||||
|
currentRegistration = registration
|
||||||
|
checkForUpdate(registration)
|
||||||
|
|
||||||
|
// waiting yalnızca aktif worker'dan farklı, kurulumu tamamlanmış gerçek
|
||||||
|
// bir worker olduğunda bulunur. Onu doğrudan etkinleştir.
|
||||||
|
if (registration.waiting && navigator.serviceWorker.controller) {
|
||||||
|
activateWaitingWorker(registration, activateUpdate, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNeedRefresh() {
|
||||||
|
// Bu callback worker'ın bütün yeni JS/CSS dosyalarını başarıyla precache'e
|
||||||
|
// almasından sonra çalışır; aktivasyon ve reload atomik yapılır.
|
||||||
|
activateWaitingWorker(currentRegistration, activateUpdate, true)
|
||||||
|
},
|
||||||
|
onNeedReload() {
|
||||||
|
reloadForUpdate()
|
||||||
|
},
|
||||||
onOfflineReady() {
|
onOfflineReady() {
|
||||||
console.log('📦 App offline ready')
|
console.log('📦 App offline ready')
|
||||||
},
|
},
|
||||||
|
onRegisterError(error) {
|
||||||
|
activationInProgress = false
|
||||||
|
clearActivationTimers()
|
||||||
|
hideUpdateOverlay()
|
||||||
|
console.error('Service worker registration failed.', error)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
activateUpdateHandler = activateUpdate
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const checkForAppUpdate = async (): Promise<ManualUpdateResult> => {
|
||||||
|
if (!('serviceWorker' in navigator)) return 'unsupported'
|
||||||
|
|
||||||
|
// App içindeki normal kayıt işlemi henüz bitmediyse önce onu tamamla.
|
||||||
|
await registrationInitialization
|
||||||
|
const registration =
|
||||||
|
currentRegistration ?? (await navigator.serviceWorker.ready.catch(() => undefined))
|
||||||
|
|
||||||
|
if (!registration) return 'unsupported'
|
||||||
|
|
||||||
|
let updateFound = false
|
||||||
|
const handleUpdateFound = () => {
|
||||||
|
updateFound = true
|
||||||
|
}
|
||||||
|
registration.addEventListener('updatefound', handleUpdateFound)
|
||||||
|
try {
|
||||||
|
await registration.update()
|
||||||
|
} finally {
|
||||||
|
registration.removeEventListener('updatefound', handleUpdateFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAvailable = Boolean(updateFound || registration.installing || registration.waiting)
|
||||||
|
if (!updateAvailable) return 'up-to-date'
|
||||||
|
|
||||||
|
if (registration.waiting && activateUpdateHandler) {
|
||||||
|
activateWaitingWorker(registration, activateUpdateHandler, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// installing durumundaysa gerçek onNeedRefresh callback'i indirme bittikten
|
||||||
|
// sonra aynı overlay/aktivasyon akışını başlatır.
|
||||||
|
return 'updating'
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@ export default defineConfig(async ({ mode }) => {
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
// Deploy'dan sonra otomatik güncelle
|
// Yeni sürüm önce tamamen indirilir, ardından uygulama kontrollü olarak
|
||||||
registerType: 'autoUpdate',
|
// yeni service worker'a geçer. Böylece eski ekran ile yeni chunk'lar karışmaz.
|
||||||
// Kayıt kodunu otomatik enjekte et (virtual:pwa-register yazmadan da çalışır)
|
registerType: 'prompt',
|
||||||
injectRegister: 'auto',
|
// Kayıt ve loading akışı src/views/version/swRegistration.ts tarafından yönetilir.
|
||||||
|
injectRegister: false,
|
||||||
// Dev ortamında SW'yi aç, prod'da kapalı tut (build edilmiş SW prod'da zaten aktif olur)
|
// Dev ortamında SW'yi aç, prod'da kapalı tut (build edilmiş SW prod'da zaten aktif olur)
|
||||||
devOptions: {
|
devOptions: {
|
||||||
enabled: mode !== 'production',
|
enabled: mode !== 'production',
|
||||||
|
|
@ -38,9 +39,10 @@ export default defineConfig(async ({ mode }) => {
|
||||||
// Büyük asset'leri de cache'leyebil
|
// Büyük asset'leri de cache'leyebil
|
||||||
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
|
maximumFileSizeToCacheInBytes: 15 * 1024 * 1024,
|
||||||
|
|
||||||
// EN KRİTİK: yeni SW beklemeden kontrolü alsın
|
// Yeni worker tüm precache dosyalarını indirdikten sonra uygulama
|
||||||
|
// SKIP_WAITING mesajını gönderir. clientsClaim açık sekmeleri o anda yeniler.
|
||||||
clientsClaim: true,
|
clientsClaim: true,
|
||||||
skipWaiting: true,
|
skipWaiting: false,
|
||||||
|
|
||||||
// Eski workbox cache'lerini temizle
|
// Eski workbox cache'lerini temizle
|
||||||
cleanupOutdatedCaches: true,
|
cleanupOutdatedCaches: true,
|
||||||
|
|
@ -48,14 +50,14 @@ export default defineConfig(async ({ mode }) => {
|
||||||
// SPA fallback'i API çağrılarına uygulama
|
// SPA fallback'i API çağrılarına uygulama
|
||||||
navigateFallbackDenylist: [/^\/api\//],
|
navigateFallbackDenylist: [/^\/api\//],
|
||||||
|
|
||||||
// ⭐⭐ BU KISMI EKLEYİN: Cache sorununu çözecek runtime caching
|
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
urlPattern: /\.(?:js|css|wasm)$/,
|
urlPattern: /\.(?:js|css|wasm)$/,
|
||||||
handler: 'NetworkFirst',
|
// Hash'li build dosyaları immutable'dır. Normal kullanımda ağ
|
||||||
|
// beklenmez; deploy güncellemesini precache manifest'i yönetir.
|
||||||
|
handler: 'CacheFirst',
|
||||||
options: {
|
options: {
|
||||||
cacheName: 'static-resources-v2',
|
cacheName: 'static-resources-v2',
|
||||||
networkTimeoutSeconds: 5,
|
|
||||||
expiration: {
|
expiration: {
|
||||||
maxEntries: 200,
|
maxEntries: 200,
|
||||||
maxAgeSeconds: 30 * 24 * 60 * 60,
|
maxAgeSeconds: 30 * 24 * 60 * 60,
|
||||||
|
|
@ -66,13 +68,14 @@ export default defineConfig(async ({ mode }) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
urlPattern: /\.json$/,
|
// Changelog/versiyon bilgisi cache yüzünden eski kalmamalı.
|
||||||
|
urlPattern: /\/version\.json$/,
|
||||||
handler: 'NetworkFirst',
|
handler: 'NetworkFirst',
|
||||||
options: {
|
options: {
|
||||||
cacheName: 'json-resources-v1',
|
cacheName: 'app-version-v1',
|
||||||
networkTimeoutSeconds: 5,
|
networkTimeoutSeconds: 3,
|
||||||
expiration: {
|
expiration: {
|
||||||
maxEntries: 50,
|
maxEntries: 5,
|
||||||
maxAgeSeconds: 24 * 60 * 60,
|
maxAgeSeconds: 24 * 60 * 60,
|
||||||
},
|
},
|
||||||
cacheableResponse: {
|
cacheableResponse: {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue