diff --git a/api/src/Kurs.Platform.Application/PlatformApplicationModule.cs b/api/src/Kurs.Platform.Application/PlatformApplicationModule.cs index b3dda99c..ce0e4c72 100644 --- a/api/src/Kurs.Platform.Application/PlatformApplicationModule.cs +++ b/api/src/Kurs.Platform.Application/PlatformApplicationModule.cs @@ -3,6 +3,7 @@ using Kurs.Notifications.Application; using Kurs.Settings; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Account; +using Volo.Abp.Auditing; using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; @@ -39,5 +40,11 @@ public class PlatformApplicationModule : AbpModule options.IsDynamicPermissionStoreEnabled = true; options.SaveStaticPermissionsToDatabase = true; }); + + // ListFormCustomization için audit kaydı kapatılıyor + Configure(options => + { + options.IgnoredTypes.Add(typeof(Entities.ListFormCustomization)); + }); } } diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/Sector.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/Sector.cs index 8a342fbb..1c21f7c8 100644 --- a/api/src/Kurs.Platform.Domain/Entities/Tenant/Sector.cs +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/Sector.cs @@ -9,7 +9,6 @@ public class Sector : FullAuditedEntity, IMultiTenant public Guid? TenantId { get; set; } public string Name { get; set; } - public string FullName { get; set; } Guid? IMultiTenant.TenantId => TenantId; } \ No newline at end of file diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index aa23f298..8de2167c 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -750,7 +750,6 @@ public class PlatformDbContext : b.ConfigureByConvention(); b.Property(x => x.Name).IsRequired().HasMaxLength(128); - b.Property(x => x.FullName).HasMaxLength(256); }); builder.Entity(b => diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.Designer.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.Designer.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.Designer.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.Designer.cs index e1ea814a..13243638 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.Designer.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Kurs.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20251016215302_Initial")] + [Migration("20251018150230_Initial")] partial class Initial { /// @@ -6070,10 +6070,6 @@ namespace Kurs.Platform.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); - b.Property("FullName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.cs index d927cfea..34021f47 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251016215302_Initial.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251018150230_Initial.cs @@ -1068,7 +1068,6 @@ namespace Kurs.Platform.Migrations Id = table.Column(type: "uniqueidentifier", nullable: false), TenantId = table.Column(type: "uniqueidentifier", nullable: true), Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), - FullName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), CreationTime = table.Column(type: "datetime2", nullable: false), CreatorId = table.Column(type: "uniqueidentifier", nullable: true), LastModificationTime = table.Column(type: "datetime2", nullable: true), diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 30fdff96..1f03ffed 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -6067,10 +6067,6 @@ namespace Kurs.Platform.Migrations .HasColumnType("datetime2") .HasColumnName("DeletionTime"); - b.Property("FullName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json index 6395169b..85273de5 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/Seeds/TenantData.json @@ -100,7 +100,9 @@ "props": null, "description": null, "isActive": true, - "dependencies": ["AxiosListComponent"] + "dependencies": [ + "AxiosListComponent" + ] } ], "ReportCategories": [ @@ -120,7 +122,6 @@ "icon": "📜" } ], - "Abouts": [ { "stats": [ @@ -623,65 +624,97 @@ ], "Sectors": [ { - "Name": "Ambalaj", - "FullName": "" + "Name": "Adalet ve Güvenlik" }, { - "Name": "Demir Çelik", - "FullName": "" + "Name": "Ağaç İşleri, Kağıt ve Kağıt Ürünleri" + }, + { + "Name": "Ambalaj" + }, + { + "Name": "Bilişim Teknolojileri" + }, + { + "Name": "Cam, Çimento ve Toprak" + }, + { + "Name": "Çevre" + }, + { + "Name": "Demir Çelik" }, { "Name": "Diğer", "FullName": "" }, + { + "Name": "Eğitim" + }, { "Name": "Elektrik ve Elektronik", "FullName": "" }, { - "Name": "Giyim", + "Name": "Enerji" + }, + { + "Name": "Finans", "FullName": "" }, { - "Name": "Güvenlik", - "FullName": "" + "Name": "Gıda" }, { - "Name": "Gıda", - "FullName": "" + "Name": "Giyim" + }, + { + "Name": "Güvenlik" }, { "Name": "Hizmet-servis", "FullName": "" }, { - "Name": "Hırdavat ve Nalburiye", - "FullName": "" + "Name": "Hırdavat ve Nalburiye" }, { "Name": "Isıtma, Soğutma ve Havalandırma", "FullName": "" }, { - "Name": "İnşaat", - "FullName": "" + "Name": "İnşaat" + }, + { + "Name": "İş ve Yönetim" }, { "Name": "Kantar", "FullName": "" }, + { + "Name": "Kimya, Petrol, Lastik ve Plastik" + }, { "Name": "Kimyasal", "FullName": "" }, { - "Name": "Kırtasiye", - "FullName": "" + "Name": "Kırtasiye" + }, + { + "Name": "Kültür, Sanat ve Tasarım" }, { "Name": "Laboratuar ve Test Ürünleri", "FullName": "" }, + { + "Name": "Maden" + }, + { + "Name": "Makine" + }, { "Name": "Makina", "FullName": "" @@ -690,37 +723,67 @@ "Name": "Matbaa", "FullName": "" }, + { + "Name": "Medya, İletişim ve Yayıncılık" + }, + { + "Name": "Metal" + }, { "Name": "Ofis", "FullName": "" }, + { + "Name": "Otomotiv" + }, { "Name": "Oto Tamir-Servis", "FullName": "" }, { - "Name": "Pnomatik", - "FullName": "" - }, - { - "Name": "Sarf", - "FullName": "" + "Name": "Pnomatik" }, { "Name": "Sağlık", "FullName": "" }, { - "Name": "Tartı", + "Name": "Sağlık ve Sosyal Hizmetler" + }, + { + "Name": "Sarf" + }, + { + "Name": "Spor ve Rekreasyon", "FullName": "" }, { - "Name": "Transpalet", + "Name": "Tarım, Avcılık ve Balıkçılık" + }, + { + "Name": "Tartı" + }, + { + "Name": "Tekstil, Hazır Giyim, Deri", "FullName": "" }, { - "Name": "Yedek Parça", - "FullName": "" + "Name": "Ticaret (Satış ve Pazarlama)" + }, + { + "Name": "Toplumsal ve Kişisel Hizmetler" + }, + { + "Name": "Transpalet" + }, + { + "Name": "Turizm, Konaklama, Yiyecek-İçecek Hizmetleri" + }, + { + "Name": "Ulaştırma, Lojistik ve Haberleşme" + }, + { + "Name": "Yedek Parça" } ], "SkillTypes": [ @@ -1884,4 +1947,4 @@ "Explanation": "" } ] -} +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs index 20e401b2..f6f5d290 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantDataSeeder.cs @@ -192,7 +192,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency await _sectorRepository.InsertAsync(new Sector { Name = item.Name, - FullName = item.FullName }); } } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs index 5fbc82d2..9d07461f 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Tenants/TenantSeederDto.cs @@ -62,7 +62,6 @@ public class GlobalSearchSeedDto public class SectorSeedDto { public string Name { get; set; } - public string FullName { get; set; } } public class UomCategorySeedDto diff --git a/api/src/Kurs.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/api/src/Kurs.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index d50167b8..f43e3fdd 100644 --- a/api/src/Kurs.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/api/src/Kurs.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -27,6 +27,7 @@ using OpenIddict.Server.AspNetCore; using OpenIddict.Validation.AspNetCore; using Volo.Abp; using Volo.Abp.Account.Web; +using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.Mvc; @@ -35,6 +36,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling; using Volo.Abp.AspNetCore.Serilog; +using Volo.Abp.Auditing; using Volo.Abp.Autofac; using Volo.Abp.BackgroundWorkers.Hangfire; using Volo.Abp.BlobStoring; @@ -112,6 +114,7 @@ public class PlatformHttpApiHostModule : AbpModule ConfigureCache(); ConfigureHangfire(context, configuration); ConfigureBlobStoring(configuration); + ConfigureAuditing(); context.Services.AddSignalR(); @@ -351,6 +354,13 @@ public class PlatformHttpApiHostModule : AbpModule }); } + private void ConfigureAuditing() + { + Configure(options => + { + options.IgnoredUrls.Add("/api/app/list-form-customization"); + }); + } public override void OnApplicationInitialization(ApplicationInitializationContext context) { diff --git a/ui/src/views/admin/auditLog/AuditLogDetail.tsx b/ui/src/views/admin/auditLog/AuditLogDetail.tsx index b1e951fc..2bfa1ebf 100644 --- a/ui/src/views/admin/auditLog/AuditLogDetail.tsx +++ b/ui/src/views/admin/auditLog/AuditLogDetail.tsx @@ -3,10 +3,19 @@ import Dialog from '@/components/ui/Dialog' import TabList from '@/components/ui/Tabs/TabList' import TabNav from '@/components/ui/Tabs/TabNav' import TabContent from '@/components/ui/Tabs/TabContent' -import { Tabs } from '@/components/ui' +import { Tabs, Badge, Spinner } from '@/components/ui' import { AdaptableCard } from '@/components/shared' import { AuditLogDto } from '@/proxy/auditLog/audit-log' import { getAuditLogs } from '@/services/identity.service' +import { + HiOutlineClock, + HiOutlineGlobe, + HiOutlineUser, + HiOutlineCode, + HiOutlineDocumentText, + HiOutlineExclamationCircle, + HiOutlineCheckCircle, +} from 'react-icons/hi' function AuditLogs({ open, @@ -18,6 +27,7 @@ function AuditLogs({ id: string }) { const [selectedLog, setSelectedLog] = useState() + const [loading, setLoading] = useState(false) useEffect(() => { if (open && id) { @@ -26,184 +36,362 @@ function AuditLogs({ }, [open, id]) const fetchAuditLog = async (logId: string) => { - const response = await getAuditLogs(logId) - setSelectedLog(response.data) + setLoading(true) + try { + const response = await getAuditLogs(logId) + setSelectedLog(response.data) + } catch (error) { + console.error('Failed to fetch audit log:', error) + } finally { + setLoading(false) + } } - return ( - -
- Audit Log Detail - {selectedLog?.id && ( - ({selectedLog.id}) - )} -
+ const getStatusBadge = (statusCode?: number) => { + if (!statusCode) return Unknown + if (statusCode >= 200 && statusCode < 300) + return + if (statusCode >= 400 && statusCode < 500) + return + if (statusCode >= 500) return + return + } - {!selectedLog ? ( -
Loading...
+ const getChangeTypeBadge = (changeType: number) => { + const types = ['Created', 'Updated', 'Deleted'] + const colors = ['bg-green-600', 'bg-blue-600', 'bg-red-600'] + return ( + + ) + } + + const formatDuration = (ms: number) => { + if (ms < 1000) return `${ms}ms` + return `${(ms / 1000).toFixed(2)}s` + } + + const InfoRow = ({ icon: Icon, label, value, valueClassName = '' }: any) => ( +
+ +
+
{label}
+
{value}
+
+
+ ) + + return ( + + {/* Header */} +
+
+

Audit Log Details

+ {selectedLog?.id &&

ID: {selectedLog.id}

} +
+ {selectedLog?.httpStatusCode && ( +
+ {getStatusBadge(selectedLog.httpStatusCode)} + +
+ )} +
+ + {loading ? ( +
+ +
+ ) : !selectedLog ? ( +
+ +

No audit log found

+
) : ( - - Log + + + + Overview + - Actions {selectedLog.actions?.length > 0 && `(${selectedLog.actions.length})`} + + Actions + {selectedLog.actions?.length > 0 && ( + + )} - Entity Changes{' '} - {selectedLog.entityChanges?.length > 0 && `(${selectedLog.entityChanges.length})`} + + Entity Changes + {selectedLog.entityChanges?.length > 0 && ( + + )} - {/* LOG DETAILS */} + {/* OVERVIEW TAB */} -
- -
Request Info
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
User{selectedLog.userName}
Time{new Date(selectedLog.executionTime).toLocaleString()}
Duration{selectedLog.executionDuration} ms
IP{selectedLog.clientIpAddress}
Method{selectedLog.httpMethod}
Status Code{selectedLog.httpStatusCode}
URL{selectedLog.url}
+
+ {/* Request Information */} + +
+ + Request Information +
+
+ + + 1000 + ? 'text-orange-600 font-semibold' + : 'text-green-600' + } + /> + +
- -
Client Info
- - - - - - - - - - - -
Browser{selectedLog.browserInfo}
Exceptions -
-                          {selectedLog.exceptions || 'None'}
-                        
-
+ {/* HTTP Details */} + +
+ + HTTP Details +
+
+ + } + /> + + + + {selectedLog.browserInfo || 'Unknown'} + + } + /> +
+ + {/* Exceptions (Full Width) */} + {selectedLog.exceptions && ( + +
+ + Exceptions +
+
+                    {selectedLog.exceptions}
+                  
+
+ )} + + {/* Comments */} + {selectedLog.comments && ( + +
+ + Comments +
+

{selectedLog.comments}

+
+ )}
- {/* ACTIONS */} + {/* ACTIONS TAB */} - {selectedLog.actions?.map((action, index) => ( - -
Action #{index + 1}
- - - - - - - - - - - - - - - - - - - - - - - -
Service{action.serviceName}
Method{action.methodName}
Time{action.executionTime}
Duration{action.executionDuration} ms
Parameters -
-                          {action.parameters}
+            {!selectedLog.actions || selectedLog.actions.length === 0 ? (
+              
+ +

No actions recorded

+
+ ) : ( +
+ {selectedLog.actions.map((action, index) => ( + +
+
+ + {action.methodName} +
+ +
+ +
+
+

Service Name

+

+ {action.serviceName} +

+
+
+

Execution Time

+

+ {new Date(action.executionTime).toLocaleString('tr-TR')} +

+
+
+ + {action.parameters && ( +
+

Parameters

+
+                          {JSON.stringify(JSON.parse(action.parameters), null, 2)}
                         
-
-
- ))} +
+ )} + + ))} + + )}
- {/* ENTITY CHANGES */} + {/* ENTITY CHANGES TAB */} - {selectedLog.entityChanges?.map((change, index) => ( - -
Change #{index + 1}
- - - - - - - - - - - - - - - - - - - - - - - -
Time{new Date(change.changeTime).toLocaleString()}
Type{change.changeType}
Entity ID{change.entityId}
Entity Type{change.entityTypeFullName}
Property Changes -
    - {change.propertyChanges.map((p, i) => ( -
  • - {p.propertyName} ({p.propertyTypeFullName}):{' '} - {!p.originalValue || p.originalValue === 'null' ? ( - - {p.newValue} - - ) : ( - <> - {p.originalValue}{' '} - - - {p.newValue} + {!selectedLog.entityChanges || selectedLog.entityChanges.length === 0 ? ( +
    + +

    No entity changes recorded

    +
    + ) : ( +
    + {selectedLog.entityChanges.map((change, index) => ( + +
    +
    + +
    +
    + {change.entityTypeFullName} +
    +

    ID: {change.entityId}

    +
    +
    +
    + {getChangeTypeBadge(change.changeType)} + + {new Date(change.changeTime).toLocaleTimeString('tr-TR')} + +
    +
    + + {change.propertyChanges && change.propertyChanges.length > 0 && ( +
    +

    Property Changes

    +
    + {change.propertyChanges.map((prop, i) => ( +
    +
    +
    + + {prop.propertyName} + + + ({prop.propertyTypeFullName?.split('.').pop()}) - - )} -
  • + +
    + {!prop.originalValue || + prop.originalValue === 'null' || + prop.originalValue === '[Not Tracked]' ? ( + + {prop.newValue || 'null'} + + ) : ( + <> + + {prop.originalValue} + + + + {prop.newValue || 'null'} + + + )} +
    + + ))} -
-
-
- ))} + + + )} + + ))} + + )}
)}