My Profile düzenlemesi
This commit is contained in:
parent
10abf9c08d
commit
2d59f7116a
20 changed files with 971 additions and 47 deletions
|
|
@ -1273,10 +1273,10 @@ soneklerini alır.
|
||||||
| **Public › Blog Category / Posts** | `App.BlogManagement.Category` · `.Posts` | `Sas_H_BlogCategory` · `Sas_H_BlogPost` | host | Çok dilli içerik (`ContentTr`/`ContentEn`), slug, yayın durumu, sayaç alanları; public `/blog` bu tablolardan okur, yayınlanmış yazılar sitemap'e girer. |
|
| **Public › Blog Category / Posts** | `App.BlogManagement.Category` · `.Posts` | `Sas_H_BlogCategory` · `Sas_H_BlogPost` | host | Çok dilli içerik (`ContentTr`/`ContentEn`), slug, yayın durumu, sayaç alanları; public `/blog` bu tablolardan okur, yayınlanmış yazılar sitemap'e girer. |
|
||||||
| **Public › Demos** | `App.Demos` | `Sas_H_Demo` | host | Public `/demo` formundan gelen talepler — public form → tablo akışının örneği. |
|
| **Public › Demos** | `App.Demos` | `Sas_H_Demo` | host | Public `/demo` formundan gelen talepler — public form → tablo akışının örneği. |
|
||||||
| **Tenants** | `AbpTenantManagement.Tenants` | `AbpTenants` | host | Kiracı kaydı, bağlantı dizesi diyaloğu (`TenantsConnectionString`), alt form olarak şubeler; yazma `list-form-dynamic-api/tenant-*`. Ekranların `IsTenant` davranışının kaynağı. |
|
| **Tenants** | `AbpTenantManagement.Tenants` | `AbpTenants` | host | Kiracı kaydı, bağlantı dizesi diyaloğu (`TenantsConnectionString`), alt form olarak şubeler; yazma `list-form-dynamic-api/tenant-*`. Ekranların `IsTenant` davranışının kaynağı. |
|
||||||
| **Branches** | `App.Branches` | `Sas_T_Branch` | tenant | Şube listesi. Ekranı `IsBranch = true` yaparsan filtre bu tablodan gelir; `BranchSeed` diyaloğu şube verisini kurar. |
|
| **Branches** | `App.Branches` | `Sas_T_Branch` | tenant | Şube listesi. Ekranı `IsBranch = true` yaparsan filtre bu tablodan gelir; `BranchSeed` diyaloğu şube verisini kurar; `App.Branches.Update` ister ve kiracı yalnızca kendi şubesini kurabilir. |
|
||||||
| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions` (`List` tipinde değer → etiket; etiket dil anahtarıdır, Ayarlar ekranı `::` önekiyle çevirir, anahtar yoksa metin olduğu gibi görünür), `DefaultValue`, `Providers`, `RequiredPermissionName`, `IsEncrypted`, `IsVisibleToClients`. Hazır tanımlar `data/App.SettingDefinitions.json`'dan gelir. Ayarlanabilir olması gereken değeri koda gömme. |
|
| **Setting Definitions** | `App.SettingDefinitions` | `Sas_H_SettingDefinition` | host | Yeni uygulama ayarı: `Code`, `MainGroupKey`/`SubGroupKey`, `DataType`, `SelectOptions` (`List` tipinde değer → etiket; etiket dil anahtarıdır, Ayarlar ekranı `::` önekiyle çevirir, anahtar yoksa metin olduğu gibi görünür), `DefaultValue`, `Providers`, `RequiredPermissionName`, `IsEncrypted`, `IsVisibleToClients`. Hazır tanımlar `data/App.SettingDefinitions.json`'dan gelir. Ayarlanabilir olması gereken değeri koda gömme. |
|
||||||
| **Languages › Language / Language Text** | `App.Languages.Language` · `.LanguageText` | `Sas_H_Language` · `Sas_H_LanguageKey` · `Sas_H_LanguageText` | host | Dil anahtarları. Üretilen her ekranın EN+TR metinleri buraya yazılır; ekran `data/App.Languages.{Language,LanguageText}.json` dosyalarına senkronlanır. Metin insert'i eksik `LanguageKey`'i de üretir, aynı kültür+kaynak+anahtar varsa eklemez. |
|
| **Languages › Language / Language Text** | `App.Languages.Language` · `.LanguageText` | `Sas_H_Language` · `Sas_H_LanguageKey` · `Sas_H_LanguageText` | host | Dil anahtarları. Üretilen her ekranın EN+TR metinleri buraya yazılır; ekran `data/App.Languages.{Language,LanguageText}.json` dosyalarına senkronlanır. Metin insert'i eksik `LanguageKey`'i de üretir, aynı kültür+kaynak+anahtar varsa eklemez. |
|
||||||
| **Data Source** | `App.DataSource` | `Sas_H_DataSource` | host | `Code`, `DataSourceType`, `ConnectionString`. **İki tür kayıt bir arada:** elle tanımlanan host/harici bağlantılar ve `Code = tenant adı` olan otomatik tenant bağlantıları (Tenants ekranındaki bağlantı diyaloğu yönetir, elle dokunma). Varsayılan kaydı `HostDataSeeder` appsettings'teki bağlantı cümlesinden kurar. Çözüm kuralı `lowcode` §9.3.3. Bağlantı dizesi koda/JSON'a yazılmaz. |
|
| **Data Source** | `App.DataSource` | `Sas_H_DataSource` | host | `Code`, `DataSourceType`, `ConnectionString`. Tipli API (`/api/app/data-source`) bağlantı cümlesini hiçbir zaman döndürmez; okuma açıktır (açılır listeler), yazma `App.DataSource.Create/Update/Delete` ister. **İki tür kayıt bir arada:** elle tanımlanan host/harici bağlantılar ve `Code = tenant adı` olan otomatik tenant bağlantıları (Tenants ekranındaki bağlantı diyaloğu yönetir, elle dokunma). Varsayılan kaydı `HostDataSeeder` appsettings'teki bağlantı cümlesinden kurar. Çözüm kuralı `lowcode` §9.3.3. Bağlantı dizesi koda/JSON'a yazılmaz. |
|
||||||
| **Background Workers › Recurring Jobs / Jobs** | `App.BackgroundWorkers.RecurringJobs` · `.Jobs` | `Sas_H_BackgroundWorker` · `Sas_H_BackgroundWorker_JobFlow` | host | Cron'lu işler ve çalıştırma geçmişi (`Tenant`, `ExecutionDate`, `Duration`, `Status`, `FailureReason`, `ExceptionDetails`). `WorkerType`/`Scope` değerleri §11'de; her kapsam ayrı bir Hangfire işidir, `Tenant` kolonu hangi kiracıda çalıştığını gösterir. Hazır işler (oturum temizliği, bildirim, yedek, şema sözlüğü) `data/App.BackgroundWorkers.RecurringJobs.json`'dan gelir. Ayrıntı: `lowcode` §9.10, `README.md` §14. |
|
| **Background Workers › Recurring Jobs / Jobs** | `App.BackgroundWorkers.RecurringJobs` · `.Jobs` | `Sas_H_BackgroundWorker` · `Sas_H_BackgroundWorker_JobFlow` | host | Cron'lu işler ve çalıştırma geçmişi (`Tenant`, `ExecutionDate`, `Duration`, `Status`, `FailureReason`, `ExceptionDetails`). `WorkerType`/`Scope` değerleri §11'de; her kapsam ayrı bir Hangfire işidir, `Tenant` kolonu hangi kiracıda çalıştığını gösterir. Hazır işler (oturum temizliği, bildirim, yedek, şema sözlüğü) `data/App.BackgroundWorkers.RecurringJobs.json`'dan gelir. Ayrıntı: `lowcode` §9.10, `README.md` §14. |
|
||||||
| **Menus › Module / Routes / Menu / Manager** | `App.Menus.Module` · `.Routes` · `.Menu` · `.Manager` (`/admin/menuManager`) | `Sas_H_Module` · `Sas_H_Route` · `Sas_H_Menu` | host | Menü ve route veritabanından gelir; üretilen ekranın menü kaydı buradan açılır. `Menu` ekranı ağaç düzeninin referansıdır. Seed: `data/App.Menus.{Routes,Module,Menu}.json` — `Routes` dosyasına yalnızca var olan satırın `Seo*` alanları için dokunulur. |
|
| **Menus › Module / Routes / Menu / Manager** | `App.Menus.Module` · `.Routes` · `.Menu` · `.Manager` (`/admin/menuManager`) | `Sas_H_Module` · `Sas_H_Route` · `Sas_H_Menu` | host | Menü ve route veritabanından gelir; üretilen ekranın menü kaydı buradan açılır. `Menu` ekranı ağaç düzeninin referansıdır. Seed: `data/App.Menus.{Routes,Module,Menu}.json` — `Routes` dosyasına yalnızca var olan satırın `Seo*` alanları için dokunulur. |
|
||||||
| **Developer Kit › Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`; detayda `Description` yanında `Analysis` sekmesi (`AnalysisExpr`). MCP geliştirme döngüsünün kuyruğudur (`Backlog` → `Analysis` MCP analiz eder, `Ready to Start`'a kullanıcı alır; `Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, önce onay sorar, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, analiz `save_dev_request_analysis` ile yazılır, üretim notu `append_dev_request_note` ile `Analysis`'e eklenir — `Description` talebin yazıldığı hâliyle kalır; `lowcode.instructions.md` §0.11–§0.12). |
|
| **Developer Kit › Dev Requests** | `App.DevRequests` | `Sas_H_DevRequest` | host | **Kanban/Todo düzeninin** referansı (`DefaultLayout: todo`): `Status`, `Priority`, `SortOrder`, `Tags`, `Assignees`, `SubTasks`, `Timesheets`; detayda `Description` yanında `Analysis` sekmesi (`AnalysisExpr`). MCP geliştirme döngüsünün kuyruğudur (`Backlog` → `Analysis` MCP analiz eder, `Ready to Start`'a kullanıcı alır; `Ready to Start` → `Testing`, `Staging`'e kullanıcı alır; araç çubuğunda DB Migrate butonu vardır — `UiEvalService.ApiDbMigrate()`, önce onay sorar, `App.Setup.Migrate`; yalnızca `start_dev_request` başlatır; açıklamadaki base64 görseller MCP'ye resim içeriği olarak gider, analiz `save_dev_request_analysis` ile yazılır, üretim notu `append_dev_request_note` ile `Analysis`'e eklenir — `Description` talebin yazıldığı hâliyle kalır; `lowcode.instructions.md` §0.11–§0.12). |
|
||||||
|
|
@ -1319,7 +1319,7 @@ ekranları ABP tablolarıdır, Licence/Dev Request host tablolarını okur.
|
||||||
| **Identity › Permission Groups / Permissions** | `Abp.Identity.PermissionGroups` · `Abp.Identity.Permissions` | `AbpPermissionGroups` · `AbpPermissions` | Yetki ağacı (tree düzeni). Yeni ekranın yetkileri burada görünür; `Modules` kolonu yetkiyi modüllere bağlar (pipe ile çoklu), `Order` kardeşler arası sıradır. Seed: `data/Abp.Identity.Permission{Groups,s}.json`. |
|
| **Identity › Permission Groups / Permissions** | `Abp.Identity.PermissionGroups` · `Abp.Identity.Permissions` | `AbpPermissionGroups` · `AbpPermissions` | Yetki ağacı (tree düzeni). Yeni ekranın yetkileri burada görünür; `Modules` kolonu yetkiyi modüllere bağlar (pipe ile çoklu), `Order` kardeşler arası sıradır. Seed: `data/Abp.Identity.Permission{Groups,s}.json`. |
|
||||||
| **Identity › Claim Type** | `App.IdentityManagement.ClaimType` | `AbpClaimTypes` | Kullanıcıya ek alan (claim) tanımı: `ValueType`, `Regex`, `Required`, `IsStatic`. |
|
| **Identity › Claim Type** | `App.IdentityManagement.ClaimType` | `AbpClaimTypes` | Kullanıcıya ek alan (claim) tanımı: `ValueType`, `Regex`, `Required`, `IsStatic`. |
|
||||||
| **Identity › Roles** | `AbpIdentity.Roles` | `AbpRoles` | Rol yönetimi + `RolesPermission` diyaloğu; yeni ekranın yetkisi role buradan verilir. Yazma `list-form-dynamic-api/role-*`. |
|
| **Identity › Roles** | `AbpIdentity.Roles` | `AbpRoles` | Rol yönetimi + `RolesPermission` diyaloğu; yeni ekranın yetkisi role buradan verilir. Yazma `list-form-dynamic-api/role-*`. |
|
||||||
| **Identity › Users** | `AbpIdentity.Users` · detay `/admin/users/detail/:userId` | `AbpUsers` | Kullanıcı yönetimi + `UsersPermission` diyaloğu, **KPI widget'ları** ve `list-form-dynamic-api/user-*` ile devredilen yazma. Kullanıcı `WorkHour`, `DepartmentId`, `JobPositionId`, `Gender` (`Male`/`Female`, `App.StaticLookup.*`) ve `HomeAddress` alanlarını liste ve formda taşır; kişisel ve kimlik bilgileri (`IdentityNumber`, `BirthDate`, `EducationLevel`, `Nationality`, `SskNo`…) detay ekranındadır. Detay sekmeleri karışmasın diye gruplanmıştır: **General** (iletişim + ikinci e-posta/telefon, ek bilgi, avatar, askerlik `MilitaryStatus`/`MilitaryPostponeEndDate`, engellilik `IsDisabled`/`DisabilityRate`/`DisabilityReport` — rapor base64, 5 MB), **Identity** (nüfus cüzdanı, doğum ve aile, uyruk Türkiye değilse yabancı uyruk `Passport*`/`WorkPermit*`), **Work** (organizasyon `RegistrationNo`/`SskNo`/`ShiftId`/`PdksCardNo`, sözleşme `EmploymentType`/`ContractType`/`ContractEndDate`/`ProbationEndDate`/`SeniorityStartDate`, çıkış `TerminationDate`/`TerminationReasonId`/`TerminationNote`, sürücü belgesi `DrivingLicense*` — sınıflar `|` ile, iletişim kanalları; altında iş deneyimi), **Education & Skills** (en yüksek `EducationLevel` + eğitim/dil/yetenek/belge alt listeleri), **Family / Emergency**, **Financial** (`BankName`, `Iban`, `SalaryType`, `GrossSalary`, `SalaryCurrency` + yan haklar), Health, Permissions, Lockout, Claims. Deneme süresi işe girişten en fazla dört ay sonra biter (`UpdateUserAsync` ve ekran doğrular). Ücret alanları `UserInfoViewModel`'de taşınmaz — o model intranet doğum günü listesine de akar; `GET {id}/financial` / `PUT financial` uçlarından okunur, yetki detayınkiyle aynıdır (`AbpIdentity.Users.Update`). Aynı sebeple `DisabilityReport` yalnızca `GetByIdAsync`'te doldurulur. `ShiftId`, `TerminationReasonId`, `SalaryCurrency` seçenekleri ekranda `list-form-select/lookup` ile bu ListForm'un alan lookup'larından okunur (`useUserFieldLookup`). Detaydaki **Sağlık Kartı** sekmesi `BloodType` ile soy geçmişini (`{Mother,Father,Sibling,Child}HealthStatus` — `Alive`/`Deceased`, `App.StaticLookup.*` — ve `…HealthNote`) ve öz geçmişini (`ChronicDisease`, `TetanusVaccination`, `HepatitisVaccination`, `OtherVaccination`) taşır; sekme dört gruptur (Soy/Öz Geçmişi, Laboratuvar - Fizik Muayene `Lab*`/`Exam*`, Anamnez `Complaint*`/`Diagnosis*` + 3-10. sorular, Sonuç `FitForWork*`/`ShiftWorkFit`/`NightWorkFit`). Sonuçtaki hekim (`FitnessDoctorId`, `ShiftNightDoctorId`) wizard tablosu `Hr_T_Doctor`'dan `LookupQueryValues.TenantDoctorValues` ile seçilir; diploma ve işyeri hekimliği belgesi hekimden gösterilir, kullanıcıya yazılmaz. Bu alanların hepsi listede gizli alandır; arayüz `user-management/HealthCardTab.tsx`. Kullanıcı seçen her lookup bu bilgileri `AbpUsers`'tan okur ve kopya kolon aynı ad/tip/değer kümesiyle açılır (`lowcode.instructions.md` §0.12 madde 5). Yeni bir kullanıcı alanı = `PlatformConsts.AbpIdentity.User` + `AbpIdentityUserExtensions` + EF eşlemesi ve migration + `UserInfoViewModel` (detay/profil) + `CreateUpdateUserInput` ve `user-insert`/`user-update` (liste) + `ListFormSeeder_Administration` alanı + gerekiyorsa yönetici varsayılanı (`PlatformConsts.AbpIdentity.User.Admin*DefaultValue` + `PlatformIdentityDataSeeder`; host ve tenant Seed sekmesinin yöneticisi buradan oluşur). |
|
| **Identity › Users** | `AbpIdentity.Users` · detay `/admin/users/detail/:userId` | `AbpUsers` | Kullanıcı yönetimi + `UsersPermission` diyaloğu (detay ve kullanıcı bilgisi kaydı `AbpIdentity.Users.Update`, "Yetkili kullanıcılar" `App.Listforms.Listform.Update`, oturum sonlandırma `…IdentityManagement.Sessions` ister; istemciden gelen şube kimlikleri kiracının kendi şubelerine süzülür), **KPI widget'ları** ve `list-form-dynamic-api/user-*` ile devredilen yazma. Kullanıcı `WorkHour`, `DepartmentId`, `JobPositionId`, `Gender` (`Male`/`Female`, `App.StaticLookup.*`) ve `HomeAddress` alanlarını liste ve formda taşır; kişisel ve kimlik bilgileri (`IdentityNumber`, `BirthDate`, `EducationLevel`, `Nationality`, `SskNo`…) detay ekranındadır. Detay sekmeleri karışmasın diye gruplanmıştır: **General** (iletişim + ikinci e-posta/telefon, ek bilgi, avatar, askerlik `MilitaryStatus`/`MilitaryPostponeEndDate`, engellilik `IsDisabled`/`DisabilityRate`/`DisabilityReport` — rapor base64, 5 MB), **Identity** (nüfus cüzdanı, doğum ve aile, uyruk Türkiye değilse yabancı uyruk `Passport*`/`WorkPermit*`), **Work** (organizasyon `RegistrationNo`/`SskNo`/`ShiftId`/`PdksCardNo`, sözleşme `EmploymentType`/`ContractType`/`ContractEndDate`/`ProbationEndDate`/`SeniorityStartDate`, çıkış `TerminationDate`/`TerminationReasonId`/`TerminationNote`, sürücü belgesi `DrivingLicense*` — sınıflar `|` ile, iletişim kanalları; altında iş deneyimi), **Education & Skills** (en yüksek `EducationLevel` + eğitim/dil/yetenek/belge alt listeleri), **Family / Emergency**, **Financial** (`BankName`, `Iban`, `SalaryType`, `GrossSalary`, `SalaryCurrency` + yan haklar), Health, Permissions, Lockout, Claims. Deneme süresi işe girişten en fazla dört ay sonra biter (`UpdateUserAsync` ve ekran doğrular). Ücret alanları `UserInfoViewModel`'de taşınmaz — o model intranet doğum günü listesine de akar; `GET {id}/financial` / `PUT financial` uçlarından okunur, yetki detayınkiyle aynıdır (`AbpIdentity.Users.Update`). Aynı sebeple `DisabilityReport` yalnızca `GetByIdAsync`'te doldurulur. `ShiftId`, `TerminationReasonId`, `SalaryCurrency` seçenekleri ekranda `list-form-select/lookup` ile bu ListForm'un alan lookup'larından okunur (`useUserFieldLookup`). Detaydaki **Sağlık Kartı** sekmesi `BloodType` ile soy geçmişini (`{Mother,Father,Sibling,Child}HealthStatus` — `Alive`/`Deceased`, `App.StaticLookup.*` — ve `…HealthNote`) ve öz geçmişini (`ChronicDisease`, `TetanusVaccination`, `HepatitisVaccination`, `OtherVaccination`) taşır; sekme dört gruptur (Soy/Öz Geçmişi, Laboratuvar - Fizik Muayene `Lab*`/`Exam*`, Anamnez `Complaint*`/`Diagnosis*` + 3-10. sorular, Sonuç `FitForWork*`/`ShiftWorkFit`/`NightWorkFit`). Sonuçtaki hekim (`FitnessDoctorId`, `ShiftNightDoctorId`) wizard tablosu `Hr_T_Doctor`'dan `LookupQueryValues.TenantDoctorValues` ile seçilir; diploma ve işyeri hekimliği belgesi hekimden gösterilir, kullanıcıya yazılmaz. Bu alanların hepsi listede gizli alandır; arayüz `user-management/HealthCardTab.tsx`. Kullanıcı seçen her lookup bu bilgileri `AbpUsers`'tan okur ve kopya kolon aynı ad/tip/değer kümesiyle açılır (`lowcode.instructions.md` §0.12 madde 5). Yeni bir kullanıcı alanı = `PlatformConsts.AbpIdentity.User` + `AbpIdentityUserExtensions` + EF eşlemesi ve migration + `UserInfoViewModel` (detay/profil) + `CreateUpdateUserInput` ve `user-insert`/`user-update` (liste) + `ListFormSeeder_Administration` alanı + gerekiyorsa yönetici varsayılanı (`PlatformConsts.AbpIdentity.User.Admin*DefaultValue` + `PlatformIdentityDataSeeder`; host ve tenant Seed sekmesinin yöneticisi buradan oluşur). |
|
||||||
| **Identity › Users › Work Experience** | `App.IdentityManagement.UserWorkExperience` (menüsüz alt liste) | `Adm_T_UserWorkExperience` | Kullanıcının daha önce çalıştığı yerler: `CompanyName`, `SectorId` (Sektörler lookup'ı, `LookupQueryValues.TenantSectorValues`), `StartDate`, `EndDate`, `JobDescription` (HtmlEditor). Entity `IMultiTenant`; kullanıcı detayının Work sekmesinde `PlatformViewHost` ile `UserId` filtresiyle gömülüdür, yeni satır `UserId`'yi filtreden alır. Kendi yetkisi yoktur: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme detay sayfasının yetkisi `AbpIdentity.Users.Update`. |
|
| **Identity › Users › Work Experience** | `App.IdentityManagement.UserWorkExperience` (menüsüz alt liste) | `Adm_T_UserWorkExperience` | Kullanıcının daha önce çalıştığı yerler: `CompanyName`, `SectorId` (Sektörler lookup'ı, `LookupQueryValues.TenantSectorValues`), `StartDate`, `EndDate`, `JobDescription` (HtmlEditor). Entity `IMultiTenant`; kullanıcı detayının Work sekmesinde `PlatformViewHost` ile `UserId` filtresiyle gömülüdür, yeni satır `UserId`'yi filtreden alır. Kendi yetkisi yoktur: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme detay sayfasının yetkisi `AbpIdentity.Users.Update`. |
|
||||||
| **Identity › Users › Health Card lists** | `App.IdentityManagement.UserPeriodicExamination` · `.UserPatientProtocol` · `.UserSickLeave` (menüsüz alt listeler) | `Adm_T_UserPeriodicExamination` · `Adm_T_UserPatientProtocol` · `Adm_T_UserSickLeave` | Periyodik muayene (hekim `Hr_T_Doctor`'dan, diploma no seçimde `EditorScript.Copy` ile kopyalanır), hasta protokol defteri (`ProtocolType`/`ProtocolResult` statik lookup) ve istirahat listesi (`Days` tarihlerden `EditorScript.Days` ile hesaplanır). Entity'ler `IMultiTenant`; Sağlık Kartı sekmesinde (`HealthCardTab.tsx`) `UserId` filtresiyle gömülüdür. Yetki Work Experience ile aynıdır: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme `AbpIdentity.Users.Update`. |
|
| **Identity › Users › Health Card lists** | `App.IdentityManagement.UserPeriodicExamination` · `.UserPatientProtocol` · `.UserSickLeave` (menüsüz alt listeler) | `Adm_T_UserPeriodicExamination` · `Adm_T_UserPatientProtocol` · `Adm_T_UserSickLeave` | Periyodik muayene (hekim `Hr_T_Doctor`'dan, diploma no seçimde `EditorScript.Copy` ile kopyalanır), hasta protokol defteri (`ProtocolType`/`ProtocolResult` statik lookup) ve istirahat listesi (`Days` tarihlerden `EditorScript.Days` ile hesaplanır). Entity'ler `IMultiTenant`; Sağlık Kartı sekmesinde (`HealthCardTab.tsx`) `UserId` filtresiyle gömülüdür. Yetki Work Experience ile aynıdır: okuma `AbpIdentity.Users`, ekleme/güncelleme/silme `AbpIdentity.Users.Update`. |
|
||||||
| **Identity › Users › Education & Skills lists** | `App.IdentityManagement.UserEducation` · `.UserLanguage` · `.UserSkill` · `.UserCertificate` (menüsüz alt listeler) | `Adm_T_UserEducation` · `Adm_T_UserLanguage` · `Adm_T_UserSkill` · `Adm_T_UserCertificate` | Eğitim geçmişi (`EducationLevel` kullanıcınınkiyle aynı değerler, `SchoolName`, `Department`, `StartDate`, `EndDate`, `IsGraduated`), yabancı dil (`SkillId` → `Languages` tipindeki `Hr_T_Skill`, `SkillLevelId` dilin tipine ait `Hr_T_SkillLevel`, cascade), yetenek (`SkillId` → `Languages` dışındaki tipler, `Level` 1-5 statik lookup, check constraint) ve yeterlilik belgesi (`CertificateId` → wizard tablosu `Hr_T_Certificate`, EF ilişkisi yok; `CertificateNo`, `IssuedBy`, `IssueDate`, `ExpiryDate`). Lookup'lar `LookupQueryValues.Tenant{LanguageSkill,SkillLevelBySkill,NonLanguageSkill,Certificate}Values`. `EducationSkillsTab.tsx`; yetki Work Experience ile aynıdır. |
|
| **Identity › Users › Education & Skills lists** | `App.IdentityManagement.UserEducation` · `.UserLanguage` · `.UserSkill` · `.UserCertificate` (menüsüz alt listeler) | `Adm_T_UserEducation` · `Adm_T_UserLanguage` · `Adm_T_UserSkill` · `Adm_T_UserCertificate` | Eğitim geçmişi (`EducationLevel` kullanıcınınkiyle aynı değerler, `SchoolName`, `Department`, `StartDate`, `EndDate`, `IsGraduated`), yabancı dil (`SkillId` → `Languages` tipindeki `Hr_T_Skill`, `SkillLevelId` dilin tipine ait `Hr_T_SkillLevel`, cascade), yetenek (`SkillId` → `Languages` dışındaki tipler, `Level` 1-5 statik lookup, check constraint) ve yeterlilik belgesi (`CertificateId` → wizard tablosu `Hr_T_Certificate`, EF ilişkisi yok; `CertificateNo`, `IssuedBy`, `IssueDate`, `ExpiryDate`). Lookup'lar `LookupQueryValues.Tenant{LanguageSkill,SkillLevelBySkill,NonLanguageSkill,Certificate}Values`. `EducationSkillsTab.tsx`; yetki Work Experience ile aynıdır. |
|
||||||
|
|
@ -1333,7 +1333,7 @@ ekranları ABP tablolarıdır, Licence/Dev Request host tablolarını okur.
|
||||||
| **Forum** | menü kodu `App.Forum` · yetki `App.ForumManagement.Publish` · `/admin/forum` | `Sas_T_Forum*` | Forumun kullanıcı tarafı. |
|
| **Forum** | menü kodu `App.Forum` · yetki `App.ForumManagement.Publish` · `/admin/forum` | `Sas_T_Forum*` | Forumun kullanıcı tarafı. |
|
||||||
|
|
||||||
Menüde görünmeyen ama route'u olan ekranlar: `/admin/dashboard`, `/admin/ai`,
|
Menüde görünmeyen ama route'u olan ekranlar: `/admin/dashboard`, `/admin/ai`,
|
||||||
`/admin/activityLog`, `/admin/changeLog`, `/admin/profile/{general,password,notification-settings}`,
|
`/admin/activityLog`, `/admin/changeLog`, `/admin/profile/{general,employee-info,leave,timesheet,performance,password,notification-settings}`,
|
||||||
`/admin/users/detail/:userId` (yetki `AbpIdentity.Users.Update`), `/admin/videoroom/list` ve
|
`/admin/users/detail/:userId` (yetki `AbpIdentity.Users.Update`), `/admin/videoroom/list` ve
|
||||||
`/admin/videoroom/room/:id`, `/admin/reports/*`, `/admin/listform/{wizard,wizardManager}`,
|
`/admin/videoroom/room/:id`, `/admin/reports/*`, `/admin/listform/{wizard,wizardManager}`,
|
||||||
`/admin/listform/edit/:listFormCode`, `/admin/developerkit/components/design/:id` ve dinamik ekran
|
`/admin/listform/edit/:listFormCode`, `/admin/developerkit/components/design/:id` ve dinamik ekran
|
||||||
|
|
@ -1404,9 +1404,9 @@ listesinde `Hr` varsa görür (modül listesi boş kiracıda süzme yoktur — `
|
||||||
| **Recruitment › Application** | `App.Hr.JobApplication` (alt formlar `App.Hr.Interview`, `App.Hr.JobOffer`) | `Hr_T_JobApplication` | tenant | Aday × ilan başvurusu (çift tekil). `Stage`: `Applied` → `Screening` → `Interview` → `Offer` → `Hired` / `Rejected`; **Todo (kanban) görünümü** aşama sütunlarıyla. Mülakat eklenince `Interview`, teklif eklenince `Offer`, teklif `Accepted` olunca `Hired`'a ilerler (after-command SQL, geri gitmez; insert sonrası komutlar yalnızca eklenen satıra `@Id` ile çalışır). `CandidateName`/`PostingTitle` kopyalanır (kanban başlığı). Ret sebebi yalnızca `Rejected` aşamasında düzenlenir. Alt form olduğu için widget yok. Wizard. |
|
| **Recruitment › Application** | `App.Hr.JobApplication` (alt formlar `App.Hr.Interview`, `App.Hr.JobOffer`) | `Hr_T_JobApplication` | tenant | Aday × ilan başvurusu (çift tekil). `Stage`: `Applied` → `Screening` → `Interview` → `Offer` → `Hired` / `Rejected`; **Todo (kanban) görünümü** aşama sütunlarıyla. Mülakat eklenince `Interview`, teklif eklenince `Offer`, teklif `Accepted` olunca `Hired`'a ilerler (after-command SQL, geri gitmez; insert sonrası komutlar yalnızca eklenen satıra `@Id` ile çalışır). `CandidateName`/`PostingTitle` kopyalanır (kanban başlığı). Ret sebebi yalnızca `Rejected` aşamasında düzenlenir. Alt form olduğu için widget yok. Wizard. |
|
||||||
| **Recruitment › Interview** | `App.Hr.Interview` (alt form `App.Hr.InterviewScore`) | `Hr_T_Interview` · `Hr_T_InterviewScore` | tenant | Mülakat planlama: tür, başlangıç/bitiş (**Scheduler** görünümü), mülakatçı, durum, öneri. Kayıt eklenince mülakatçıya `IseAlimMulakatAtandi` bildirimi SQL ile yazılır (tipin `Custom` kuralları kanalı belirler, metin `App.Hr.Interview.Notification.*`). Puan alt formu kriter başına 1–5; `OverallScore` = Σ(puan × ağırlık) / Σ ağırlık, başvurunun `AverageScore`'u mülakatların ortalaması. Wizard. |
|
| **Recruitment › Interview** | `App.Hr.Interview` (alt form `App.Hr.InterviewScore`) | `Hr_T_Interview` · `Hr_T_InterviewScore` | tenant | Mülakat planlama: tür, başlangıç/bitiş (**Scheduler** görünümü), mülakatçı, durum, öneri. Kayıt eklenince mülakatçıya `IseAlimMulakatAtandi` bildirimi SQL ile yazılır (tipin `Custom` kuralları kanalı belirler, metin `App.Hr.Interview.Notification.*`). Puan alt formu kriter başına 1–5; `OverallScore` = Σ(puan × ağırlık) / Σ ağırlık, başvurunun `AverageScore`'u mülakatların ortalaması. Wizard. |
|
||||||
| **Recruitment › Job Offer** | `App.Hr.JobOffer` | `Hr_T_JobOffer` | tenant | Teklif: `OfferNo` (`TKL-…`), başvuru seçilince pozisyon bilgileri ilandan `copy` ile dolar, brüt ücret + para birimi, yan haklar (`Hr_T_BenefitType` kodları), `Status` (`Draft`/`Sent`/`Accepted`/`Declined`/`Expired`, yanıt tarihi otomatik). İç onay **Grid Workflow** (Kadro Talebi ile aynı akış). Onaylı satırda **Teklif Mektubu** butonu (`DynamicReport`, `Raporlar` kategorisi). Wizard. |
|
| **Recruitment › Job Offer** | `App.Hr.JobOffer` | `Hr_T_JobOffer` | tenant | Teklif: `OfferNo` (`TKL-…`), başvuru seçilince pozisyon bilgileri ilandan `copy` ile dolar, brüt ücret + para birimi, yan haklar (`Hr_T_BenefitType` kodları), `Status` (`Draft`/`Sent`/`Accepted`/`Declined`/`Expired`, yanıt tarihi otomatik). İç onay **Grid Workflow** (Kadro Talebi ile aynı akış). Onaylı satırda **Teklif Mektubu** butonu (`DynamicReport`, `Raporlar` kategorisi). Wizard. |
|
||||||
| **Performance Management** | `App.Hr.Performance` dalı (yalnız İK): `PerformancePeriod`, `PerformanceReview`, `PerformanceCalibration`, `DevelopmentPlan`; tanım `App.Hr.PerformanceRating` (İK › Tanımlar). Çalışana ait `MyPerformanceReview`, `TeamPerformanceReview`, `My360Feedback` menüsüzdür; Intranet › Dashboard'daki custom widget'lardan (`MyPerformanceWidget`, `TeamPerformanceWidget`, `My360FeedbackWidget`) açılır | `Hr_T_PerformanceRating` · `Hr_T_PerformancePeriod` · `Hr_T_PerformancePeriodSkill` · `Hr_T_PerformanceReview` · `Hr_T_PerformanceGoal` · `Hr_T_PerformanceCompetency` · `Hr_T_PerformanceFeedback` · `Hr_T_PerformanceFeedbackScore` · `Hr_T_DevelopmentPlan` | tenant | Dönem → değerlendirme (hedef KPI/OKR + yetkinlik `Hr_T_Skill`/`Hr_T_SkillLevel` + isteğe bağlı 360) → kalibrasyon → gelişim planı. **Aynı tabloya persona ekranları**: İK (tümü), çalışan (`EmployeeId = '@USERID'`), yönetici (`ManagerId = '@USERID'`), 360 değerlendiricisi (`ReviewerId = '@USERID'`); kilitli alanlar İK ekranının yetkisine bağlıdır (sunucu düşürür), satır sahipliği ve aşama kuralı `Hr_S_PerformanceGuard` (before-command). Hesap ve durum geçişi `Hr_S_PerformanceReviewRecalc` / `Hr_S_PerformanceReviewAfterSave`; tamamlanan değerlendirme `Adm_T_UserSkill`'e yazar. Widget'lar veriyi ekranın kendi sorgusundan (`list-form-select/select`) okur. Çalışma şekli ve kararlar: `configs/seeds/docs/modules/HrPerformance.md`. Wizard + Custom Component (pano kutusu). |
|
| **Performance Management** | `App.Hr.Performance` dalı (yalnız İK): `PerformancePeriod`, `PerformanceReview`, `PerformanceCalibration`, `DevelopmentPlan`; tanım `App.Hr.PerformanceRating` (İK › Tanımlar). Çalışana ait `MyPerformanceReview`, `TeamPerformanceReview`, `My360Feedback` menüsüzdür; Intranet › Dashboard'daki custom widget'lardan (`MyPerformanceWidget`, `TeamPerformanceWidget`, `My360FeedbackWidget`) açılır; çalışanınkiler profilin **Performansım** sekmesinde de görünür | `Hr_T_PerformanceRating` · `Hr_T_PerformancePeriod` · `Hr_T_PerformancePeriodSkill` · `Hr_T_PerformanceReview` · `Hr_T_PerformanceGoal` · `Hr_T_PerformanceCompetency` · `Hr_T_PerformanceFeedback` · `Hr_T_PerformanceFeedbackScore` · `Hr_T_DevelopmentPlan` | tenant | Dönem → değerlendirme (hedef KPI/OKR + yetkinlik `Hr_T_Skill`/`Hr_T_SkillLevel` + isteğe bağlı 360) → kalibrasyon → gelişim planı. **Aynı tabloya persona ekranları**: İK (tümü), çalışan (`EmployeeId = '@USERID'`), yönetici (`ManagerId = '@USERID'`), 360 değerlendiricisi (`ReviewerId = '@USERID'`); kilitli alanlar İK ekranının yetkisine bağlıdır (sunucu düşürür), satır sahipliği ve aşama kuralı `Hr_S_PerformanceGuard` (before-command). Hesap ve durum geçişi `Hr_S_PerformanceReviewRecalc` / `Hr_S_PerformanceReviewAfterSave`; tamamlanan değerlendirme `Adm_T_UserSkill`'e yazar. Widget'lar veriyi ekranın kendi sorgusundan (`list-form-select/select`) okur. Çalışma şekli ve kararlar: `configs/seeds/docs/modules/HrPerformance.md`. Wizard + Custom Component (pano kutusu). |
|
||||||
| **Leave Management** | `App.Hr.Leave` dalı (yalnız İK): `LeaveRequest` (Grid Workflow: Müdür Onayı `@ManagerUserNames` → İK Onayı; üst pozisyonu olmayan çalışanda müdür adımı `RequiresManagerApproval` karşılaştırmasıyla atlanır), `LeaveBalance` (alt formlar `LeaveBalanceAdjustment`, `LeaveBalanceYear`). Çalışana ait `MyLeaveRequest`, `MyLeaveBalance` ve müdüre ait `TeamLeaveRequest` menüsüzdür; Intranet › Dashboard'daki `MyLeaveWidget` / `TeamLeaveWidget`'tan açılır; kullanıcı detayında **İzinler** sekmesi | `Hr_T_LeaveRequest` · `Hr_T_LeaveBalanceAdjustment` · görünümler `Hr_V_LeaveBalanceYear` · `Hr_V_LeaveSummary` | tenant | Gün sayısı `Hr_F_LeaveWorkDays` (Ayarlar › İnsan Kaynakları › İzin çalışma günleri + `Hr_T_WorkCalendar`); bakiye İş Kanunu md.53 kademeleri (ayarlardan) + devir + düzeltme; müdür = departmanın en üst pozisyonunun sahibi. Doğrulama, müdür çözümü, rapor ↔ `Adm_T_UserSickLeave` eşlemesi ve bildirim `Hr_S_LeaveRequestAfterSave`; satır koruması `Hr_S_LeaveRequestGuard`; Sağlık Kartı tarafı `Hr_S_LeaveSickLeaveSync`. SQL'den ayar okuma `Hr_F_HrSetting` (kiracı → genel → varsayılan). Çalışma şekli: `configs/seeds/docs/modules/HrLeave.md`. Wizard + Custom Component + küçük UI/motor eki. |
|
| **Leave Management** | `App.Hr.Leave` dalı (yalnız İK): `LeaveRequest` (Grid Workflow: Müdür Onayı `@ManagerUserNames` → İK Onayı; üst pozisyonu olmayan çalışanda müdür adımı `RequiresManagerApproval` karşılaştırmasıyla atlanır), `LeaveBalance` (alt formlar `LeaveBalanceAdjustment`, `LeaveBalanceYear`). Çalışana ait `MyLeaveRequest`, `MyLeaveBalance` ve müdüre ait `TeamLeaveRequest` menüsüzdür; Intranet › Dashboard'daki `MyLeaveWidget` / `TeamLeaveWidget`'tan açılır; çalışanınkiler profilin **İzinlerim** sekmesinde de görünür; kullanıcı detayında **İzinler** sekmesi | `Hr_T_LeaveRequest` · `Hr_T_LeaveBalanceAdjustment` · görünümler `Hr_V_LeaveBalanceYear` · `Hr_V_LeaveSummary` | tenant | Gün sayısı `Hr_F_LeaveWorkDays` (Ayarlar › İnsan Kaynakları › İzin çalışma günleri + `Hr_T_WorkCalendar`); bakiye İş Kanunu md.53 kademeleri (ayarlardan) + devir + düzeltme; müdür = departmanın en üst pozisyonunun sahibi. Doğrulama, müdür çözümü, rapor ↔ `Adm_T_UserSickLeave` eşlemesi ve bildirim `Hr_S_LeaveRequestAfterSave`; satır koruması `Hr_S_LeaveRequestGuard`; Sağlık Kartı tarafı `Hr_S_LeaveSickLeaveSync`. SQL'den ayar okuma `Hr_F_HrSetting` (kiracı → genel → varsayılan). Çalışma şekli: `configs/seeds/docs/modules/HrLeave.md`. Wizard + Custom Component + küçük UI/motor eki. |
|
||||||
| **Timesheet & Attendance** | `App.Hr.Attendance` dalı (yalnız İK): `AttendanceLog` (ham okutma; Excel import), `Timesheet` (çalışan × gün; düzenlenen gün `IsManual`), `TimesheetMonthly` (salt okunur aylık özet, alt form `Timesheet` `Id → PeriodKey`), `TimesheetPeriod` (ay yeniden hesap / kapanış). Çalışana ait `MyTimesheet` (+ alt form `MyTimesheetDay`) menüsüzdür; Intranet › Dashboard'daki `MyTimesheetWidget`'tan açılır; kullanıcı detayında **Puantaj** sekmesi | `Hr_T_AttendanceLog` · `Hr_T_Timesheet` · `Hr_T_TimesheetPeriod` · görünümler `Hr_V_AttendanceShift` · `Hr_V_TimesheetMonthly` | tenant | Hesap `Hr_S_TimesheetCalculate` (gün tipi: rapor > ücretsiz izin > `Hr_T_WorkCalendar` tatili > hafta tatili (İzin çalışma günü ayarları) > ücretli izin > okutma > eğitim katılımı (`Training`) > varsayılan çalışma / devamsız; izinli günde `LeaveTypeId`, eğitim gününde `TrainingId`; mola `MAX(vardiya, okutulan)`; geç/erken vardiya toleransı aşılırsa tamamı; fazla mesai haftalık 45 saati aşan kısım + tatil çalışması). Kişi eşlemesi `AbpUsers.PdksCardNo` / `RegistrationNo`; kartı olan çalışan ilk PDKS okutmasından itibaren okutmayla, diğerleri elle (kayıtsız gün `IsAssumed`) takip edilir. Tetikler: hareket kaydı `Hr_S_AttendanceProcess`, PDKS Custom Endpoint `Hr_S_AttendanceImport` (`/hr/attendance/`), dönem `Hr_S_TimesheetPeriodAfterSave`, gece işi `Hr_S_TimesheetNightly`, izin/rapor prosedürleri, `Hr_S_TrainingSync`, `Hr_S_WorkCalendarAfterSave`. Modüller arası çakışma kuralları (raporlu gün devamsız değildir, rapora denk gelen izin günü bakiyeye döner, kapalı aya izin/rapor yazılmaz) `HrAttendance.md` §7.1'dedir. Ayarlar `App.HrSettings.Attendance.*`. Çalışma şekli: `configs/seeds/docs/modules/HrAttendance.md`. Wizard + Custom Component + Custom Endpoint + zamanlanmış iş + küçük UI eki. |
|
| **Timesheet & Attendance** | `App.Hr.Attendance` dalı (yalnız İK): `AttendanceLog` (ham okutma; Excel import), `Timesheet` (çalışan × gün; düzenlenen gün `IsManual`), `TimesheetMonthly` (salt okunur aylık özet, alt form `Timesheet` `Id → PeriodKey`), `TimesheetPeriod` (ay yeniden hesap / kapanış). Çalışana ait `MyTimesheet` (+ alt form `MyTimesheetDay`) menüsüzdür; Intranet › Dashboard'daki `MyTimesheetWidget`'tan açılır; profilin **Puantajım** sekmesinde de görünür; kullanıcı detayında **Puantaj** sekmesi | `Hr_T_AttendanceLog` · `Hr_T_Timesheet` · `Hr_T_TimesheetPeriod` · görünümler `Hr_V_AttendanceShift` · `Hr_V_TimesheetMonthly` | tenant | Hesap `Hr_S_TimesheetCalculate` (gün tipi: rapor > ücretsiz izin > `Hr_T_WorkCalendar` tatili > hafta tatili (İzin çalışma günü ayarları) > ücretli izin > okutma > eğitim katılımı (`Training`) > varsayılan çalışma / devamsız; izinli günde `LeaveTypeId`, eğitim gününde `TrainingId`; mola `MAX(vardiya, okutulan)`; geç/erken vardiya toleransı aşılırsa tamamı; fazla mesai haftalık 45 saati aşan kısım + tatil çalışması). Kişi eşlemesi `AbpUsers.PdksCardNo` / `RegistrationNo`; kartı olan çalışan ilk PDKS okutmasından itibaren okutmayla, diğerleri elle (kayıtsız gün `IsAssumed`) takip edilir. Tetikler: hareket kaydı `Hr_S_AttendanceProcess`, PDKS Custom Endpoint `Hr_S_AttendanceImport` (`/hr/attendance/`), dönem `Hr_S_TimesheetPeriodAfterSave`, gece işi `Hr_S_TimesheetNightly`, izin/rapor prosedürleri, `Hr_S_TrainingSync`, `Hr_S_WorkCalendarAfterSave`. Modüller arası çakışma kuralları (raporlu gün devamsız değildir, rapora denk gelen izin günü bakiyeye döner, kapalı aya izin/rapor yazılmaz) `HrAttendance.md` §7.1'dedir. Ayarlar `App.HrSettings.Attendance.*`. Çalışma şekli: `configs/seeds/docs/modules/HrAttendance.md`. Wizard + Custom Component + Custom Endpoint + zamanlanmış iş + küçük UI eki. |
|
||||||
| **Org Chart** | `App.Hr.OrgChart` · `/admin/organization` | `Hr_T_Department` · `Hr_T_JobPosition` | tenant | Departman/ünvan verisinin organizasyon şeması görünümü (özel React sayfası `hr/OrgChart`, veri `api/app/org-chart`). Menü ve route aynı yetkiyi ister. |
|
| **Org Chart** | `App.Hr.OrgChart` · `/admin/organization` | `Hr_T_Department` · `Hr_T_JobPosition` | tenant | Departman/ünvan verisinin organizasyon şeması görünümü (özel React sayfası `hr/OrgChart`, veri `api/app/org-chart`). Menü ve route aynı yetkiyi ister. |
|
||||||
| **Intranet › Announcement** | `App.Hr.Intranet.Announcement` (alt formlar Like/Comment) | `Hr_T_Announcement` · `Hr_T_AnnouncementLike` · `Hr_T_AnnouncementComment` | tenant | Duyuru + beğeni + yorum: **ana kayıt / alt etkileşim** kalıbının hazır örneği. |
|
| **Intranet › Announcement** | `App.Hr.Intranet.Announcement` (alt formlar Like/Comment) | `Hr_T_Announcement` · `Hr_T_AnnouncementLike` · `Hr_T_AnnouncementComment` | tenant | Duyuru + beğeni + yorum: **ana kayıt / alt etkileşim** kalıbının hazır örneği. |
|
||||||
| **Intranet › Survey** | `App.Hr.Intranet.Survey` (alt form Question → Option; Response → Answer) | `Hr_T_Survey` · `Hr_T_SurveyQuestion` · `Hr_T_SurveyQuestionOption` · `Hr_T_SurveyResponse` · `Hr_T_SurveyAnswer` | tenant | Anket motoru; soru–seçenek–yanıt zinciri **iki seviyeli alt form** ihtiyacının referansıdır. |
|
| **Intranet › Survey** | `App.Hr.Intranet.Survey` (alt form Question → Option; Response → Answer) | `Hr_T_Survey` · `Hr_T_SurveyQuestion` · `Hr_T_SurveyQuestionOption` · `Hr_T_SurveyResponse` · `Hr_T_SurveyAnswer` | tenant | Anket motoru; soru–seçenek–yanıt zinciri **iki seviyeli alt form** ihtiyacının referansıdır. |
|
||||||
|
|
@ -1512,6 +1512,7 @@ alanları da doldurur (`lowcode` §0.9).
|
||||||
| "sadece şu rol görsün" | Administration › Identity › Roles / Permissions | Yetki kodu + ekranın `PermissionJson`'ı |
|
| "sadece şu rol görsün" | Administration › Identity › Roles / Permissions | Yetki kodu + ekranın `PermissionJson`'ı |
|
||||||
| "herkes yalnızca kendi kayıtlarını görsün" / "şu rol şu bölgeyi görsün" | ListForm editörü › Kişiselleştirme | `ServerWhere` kaydı (herkes/rol/kullanıcı; §2.5) ya da `DefaultFilter` |
|
| "herkes yalnızca kendi kayıtlarını görsün" / "şu rol şu bölgeyi görsün" | ListForm editörü › Kişiselleştirme | `ServerWhere` kaydı (herkes/rol/kullanıcı; §2.5) ya da `DefaultFilter` |
|
||||||
| "intranet ana sayfasına kutu eklensin" | Saas › Developer Kit › Intranet Widgets | `Custom` kaynaklı widget + custom component |
|
| "intranet ana sayfasına kutu eklensin" | Saas › Developer Kit › Intranet Widgets | `Custom` kaynaklı widget + custom component |
|
||||||
|
| "çalışan kendi bilgisini / izinini / puantajını görsün" | Profil (`/admin/profile/*`) + Intranet panosu | Menüsüz `App.Hr.My*` persona ekranı + widget (`lowcode` §0.12.1); profil sekmesi sabit listedir, yenisi kod değişikliğidir |
|
||||||
| "şube bazlı olsun" | Saas › Branches | `IsBranch = true` |
|
| "şube bazlı olsun" | Saas › Branches | `IsBranch = true` |
|
||||||
| "birim ağacına göre süzülsün" | Administration › Identity › Organization Units | `IsOrganizationUnit = true` |
|
| "birim ağacına göre süzülsün" | Administration › Identity › Organization Units | `IsOrganizationUnit = true` |
|
||||||
| "mesai dışında girilemesin" / "sadece ofis IP'si" | Administration › Restrictions | Work Hour · IP Restriction kaydı |
|
| "mesai dışında girilemesin" / "sadece ofis IP'si" | Administration › Restrictions | Work Hour · IP Restriction kaydı |
|
||||||
|
|
|
||||||
50
.github/instructions/lowcode.instructions.md
vendored
50
.github/instructions/lowcode.instructions.md
vendored
|
|
@ -657,7 +657,8 @@ değildir) ve içindeki satırları taşır; satırlar demo veridir (§0.10.1).
|
||||||
|
|
||||||
**Migrate** döngünün parçası değildir ve MCP'den tetiklenmez. Parti bitince yetkili host
|
**Migrate** döngünün parçası değildir ve MCP'den tetiklenmez. Parti bitince yetkili host
|
||||||
kullanıcısı Wizard Manager'daki DB Migrate'i (`/api/setup/migrate`, `Setup.Migrate`) çalıştırır;
|
kullanıcısı Wizard Manager'daki DB Migrate'i (`/api/setup/migrate`, `Setup.Migrate`) çalıştırır;
|
||||||
migrate ekranları, menü/yetki/dil satırlarını ve demo verisini kurar, Redis'i temizler ve bütün
|
migrate ekranları, menü/yetki/dil satırlarını ve demo verisini kurar, uygulamanın önbellek anahtarlarını temizler
|
||||||
|
(`DistributedCacheCleaner`; Redis'teki diğer uygulamalara dokunmaz) ve bütün
|
||||||
kiracıları dolaşır. Rapor bu adımı tek satırla belirtir.
|
kiracıları dolaşır. Rapor bu adımı tek satırla belirtir.
|
||||||
|
|
||||||
### 0.12 Talep analizi — `Backlog` → `Analysis`
|
### 0.12 Talep analizi — `Backlog` → `Analysis`
|
||||||
|
|
@ -708,6 +709,9 @@ Kısa ve karar odaklı yaz; talebi tekrar anlatma.
|
||||||
| Ara (yalnızca talep istiyorsa, her biri) | … | … / … | … | Var / Yeni |
|
| Ara (yalnızca talep istiyorsa, her biri) | … | … / … | … | Var / Yeni |
|
||||||
| Ekran | `ListFormCode` | … / … | … | Yeni |
|
| Ekran | `ListFormCode` | … / … | … | Yeni |
|
||||||
|
|
||||||
|
Çalışanın/yöneticinin kendi listesi (persona ekranı) menüye konmaz; satıra "menüsüz — Intranet
|
||||||
|
widget'ı" yazılır (§0.12.1).
|
||||||
|
|
||||||
Altında `ModuleId`, izin grubu ve route yazılır. Var olan seviyeler `list_wizards` ve menü
|
Altında `ModuleId`, izin grubu ve route yazılır. Var olan seviyeler `list_wizards` ve menü
|
||||||
seed'inden okunur, kodları oradaki gibi yazılır; sıra (`Order`) deploy'da hesaplanır, analize
|
seed'inden okunur, kodları oradaki gibi yazılır; sıra (`Order`) deploy'da hesaplanır, analize
|
||||||
yazılmaz. Seviye sayısı talepten gelir, sabit değildir: `Kök › Ekran` iki, `Kök › Ara › Ekran`
|
yazılmaz. Seviye sayısı talepten gelir, sabit değildir: `Kök › Ekran` iki, `Kök › Ara › Ekran`
|
||||||
|
|
@ -807,6 +811,36 @@ Kısa ve karar odaklı yaz; talebi tekrar anlatma.
|
||||||
ihtiyaç, §0.8, adıyla). Talebi daraltan geçici çözüm önerilmez; "Hayır" taşıyan talep yetenek
|
ihtiyaç, §0.8, adıyla). Talebi daraltan geçici çözüm önerilmez; "Hayır" taşıyan talep yetenek
|
||||||
eklenene kadar `Ready to Start`'a alınmaz.
|
eklenene kadar `Ready to Start`'a alınmaz.
|
||||||
|
|
||||||
|
### 0.12.1 Çalışan (persona) ekranları — Wizard ve MCP için
|
||||||
|
|
||||||
|
İK menüsü İK departmanının ekranlarını taşır. Çalışanın ya da yöneticinin kendisiyle ilgili listeleri
|
||||||
|
("Taleplerim", "Değerlendirmelerim", "Ekibimin …") İK menüsüne konmaz; keşif turunda bu kalıp
|
||||||
|
varsayılan olarak önerilir:
|
||||||
|
|
||||||
|
- **Ekran:** menüsüz wizard (`CreateMenu: false`), kodu `App.Hr.My{Ekran}` / `App.Hr.Team{Ekran}`,
|
||||||
|
İK ekranıyla aynı tabloya açılır. Satırları `DefaultFilter` + alan varsayılanıyla oturumdaki
|
||||||
|
kullanıcıya süzülür (`"EmployeeId" = '@USERID'`); yetki okuma yetkisidir, İK yetkisi verilmez.
|
||||||
|
Kalıp: `configs/seeds/docs/modules/HrPerformance.md` §3.2–§3.3.
|
||||||
|
- **Erişim:** Intranet panosunda `Sas_T_IntranetWidget` kaydı (`SourceType: 1`, custom component;
|
||||||
|
`PermissionName` = persona ekranının okuma yetkisi) özet gösterir ve listeye bağlanır.
|
||||||
|
- **Profil:** `/admin/profile` sekmeleri sabittir — Çalışan Bilgileri, İzinlerim
|
||||||
|
(`App.Hr.MyLeaveRequest`, `App.Hr.MyLeaveBalance`), Puantajım (`App.Hr.MyTimesheet`), Performansım
|
||||||
|
(`App.Hr.MyPerformanceReview`, `.MyPerformanceGoal`, `.MyDevelopmentPlan`, `.My360Feedback`).
|
||||||
|
Sekmeler çekirdek, **salt okunur** bileşenlerdir (`ui/src/views/admin/profile/components`): persona
|
||||||
|
ekranının satırlarını `list-form-select` ile okur, grid/ListForm çizmez; düzenle/sil yoktur. Tek
|
||||||
|
aksiyon "İzin Talebi Oluştur" (`/admin/form/App.Hr.MyLeaveRequest`, `.Create` yetkisiyle). Süreç
|
||||||
|
işlemleri (öz değerlendirme, 360 puanlama, açık talebi düzeltme) panodaki widget'ların açtığı persona
|
||||||
|
ekranında kalır; çalışanın karar verilmiş kaydı değiştirememesi sunucu tarafında guard
|
||||||
|
prosedürüyle korunur (`Hr_S_LeaveRequestGuard`, `Hr_S_PerformanceGuard`). Sekme, ekranlarından
|
||||||
|
hiçbirinin yetkisi yoksa görünmez. Yeni bir persona ekranı profile
|
||||||
|
kendiliğinden girmez: sekmeye eklemek `ui/src/views/admin/profile` kodu + rota satırıdır, yani
|
||||||
|
çerçeve dışıdır (§0.8, onay). Wizard/MCP ekranı ve widget'ı üretir; profil sekmesini üretim notunda
|
||||||
|
öneri olarak yazar.
|
||||||
|
- **Kullanıcı alanı:** çalışanın kendi kullanıcı alanları profilde `api/account/my-profile` ek
|
||||||
|
alanlarından okunur (`PlatformProfileAppService.ProfileExtraProperties`). Kullanıcıya eklenen yeni
|
||||||
|
bir alanın profilde görünmesi isteniyorsa liste ve `MyEmployeeInfo.tsx` birlikte değişir; sağlık,
|
||||||
|
ücret ve işten çıkış alanları listeye eklenmez.
|
||||||
|
|
||||||
### 0.13 Modül dokümanı — `configs/seeds/docs/modules/{Modül}.md`
|
### 0.13 Modül dokümanı — `configs/seeds/docs/modules/{Modül}.md`
|
||||||
|
|
||||||
Üretilen her modül kendi çalışma şeklini bir md dosyasında taşır; "bu ekran nasıl çalışıyor / şu
|
Üretilen her modül kendi çalışma şeklini bir md dosyasında taşır; "bu ekran nasıl çalışıyor / şu
|
||||||
|
|
@ -2177,7 +2211,7 @@ olmalıdır.
|
||||||
|
|
||||||
| Kolon | Ne yapar |
|
| Kolon | Ne yapar |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `InsertCommand` / `UpdateCommand` / `DeleteCommand` | Özel SQL komutları; boşsa platform üretir |
|
| `InsertCommand` / `UpdateCommand` / `DeleteCommand` | Özel SQL komutları; boşsa platform üretir. Üretilen silme her zaman anahtar alanla sınırlanır, `TenantId` gibi diğer parametreler yalnızca ek koşuldur (`QueryManager`) |
|
||||||
| `*BeforeCommand` / `*AfterCommand` | Komut öncesi/sonrası çalışan SQL kancaları — **stok düşme, log yazma, durum güncelleme gibi işleri kod yazmadan burada yaparsın** |
|
| `*BeforeCommand` / `*AfterCommand` | Komut öncesi/sonrası çalışan SQL kancaları — **stok düşme, log yazma, durum güncelleme gibi işleri kod yazmadan burada yaparsın** |
|
||||||
| `Insert/Update/DeleteFieldsDefaultValueJson` | Otomatik doldurulan alanlar (`@NOW`, `@USERID`, `@NEWID`, `@ID`) |
|
| `Insert/Update/DeleteFieldsDefaultValueJson` | Otomatik doldurulan alanlar (`@NOW`, `@USERID`, `@NEWID`, `@ID`) |
|
||||||
| `Insert/Update/DeleteServiceAddress` | Varsayılan `list-form-data/*`; özel endpoint ya da Dynamic Service'e yönlendirilebilir |
|
| `Insert/Update/DeleteServiceAddress` | Varsayılan `list-form-data/*`; özel endpoint ya da Dynamic Service'e yönlendirilebilir |
|
||||||
|
|
@ -3277,8 +3311,8 @@ crud/{entity}.json (component'lerin/nesnenin kulland
|
||||||
endpoint'lerinin dayandığı tablolar)
|
endpoint'lerinin dayandığı tablolar)
|
||||||
```
|
```
|
||||||
|
|
||||||
Script dosyaları sabit kombinasyondan tahmin edilmez; kapsam kökündeki script klasörleri diskten
|
Script dosyaları sabit kombinasyondan tahmin edilmez; seed kökündeki script klasörleri (`host/`
|
||||||
taranır. Script bulunamaması uyarı üretmez — her tablo seed ile yönetilmez (ABP tabloları EF
|
alt klasörleri dahil) diskten taranır. Script bulunamaması uyarı üretmez — her tablo seed ile yönetilmez (ABP tabloları EF
|
||||||
migration'larıyla kurulur); "hedefte var mı" kararını import analizi verir. Çözülemeyen bir
|
migration'larıyla kurulur); "hedefte var mı" kararını import analizi verir. Çözülemeyen bir
|
||||||
**kayıtlı referans** (alt form, `SeedFilePath`, `DataSources[].SeedFile`, component satırı)
|
**kayıtlı referans** (alt form, `SeedFilePath`, `DataSources[].SeedFile`, component satırı)
|
||||||
indirmeyi engellemez ama sessiz de kalmaz: `ExportAnalysis` ucu arşivin tam içeriğini ve eksikleri
|
indirmeyi engellemez ama sessiz de kalmaz: `ExportAnalysis` ucu arşivin tam içeriğini ve eksikleri
|
||||||
|
|
@ -4363,8 +4397,9 @@ Dört ekran, tamamı **Administration › Bildirimler** altındadır: **Notifica
|
||||||
kanaldan alacak), **Notification** (üretilmiş bildirimler / geçmiş **ve** elle gönderim formu —
|
kanaldan alacak), **Notification** (üretilmiş bildirimler / geçmiş **ve** elle gönderim formu —
|
||||||
kod tekildir: `App.Notifications.Notification`). Dört tablo da kiracı kapsamındadır
|
kod tekildir: `App.Notifications.Notification`). Dört tablo da kiracı kapsamındadır
|
||||||
(`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`, `Adm_T_NotificationRule`,
|
(`Adm_T_NotificationTemplate`, `Adm_T_NotificationType`, `Adm_T_NotificationRule`,
|
||||||
`Adm_T_Notification`); yeni kiracıya host seed dosyalarındaki tür ve kural kümesi yeni kimliklerle
|
`Adm_T_Notification`); kendi veritabanı olan kiracı tür ve kuralları seed dosyasından alır; host veritabanını paylaşan
|
||||||
kopyalanır (`NotificationTenantDefaultsSeedContributor`, `README.md` §13.5.1).
|
kiracıya aynı dosyalardaki küme yeni kimliklerle kopyalanır (`NotificationTenantDefaultsSeedContributor`,
|
||||||
|
`README.md` §13.5.1).
|
||||||
|
|
||||||
| `NotificationRule` alanı | Anlam |
|
| `NotificationRule` alanı | Anlam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
|
@ -4448,7 +4483,7 @@ platformun hazır aksiyonları buradan çağrılır. Uydurma metot yazma; kullan
|
||||||
| Çağrı | Ne yapar | Sunucu yetkisi |
|
| Çağrı | Ne yapar | Sunucu yetkisi |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `UiEvalService.ApiGenerateBackgroundWorkers()` | Zamanlanmış iş tanımlarını yeniden üretir | — |
|
| `UiEvalService.ApiGenerateBackgroundWorkers()` | Zamanlanmış iş tanımlarını yeniden üretir | — |
|
||||||
| `UiEvalService.ApiClearRedisCache()` | Dağıtık önbelleği temizler | — |
|
| `UiEvalService.ApiClearRedisCache()` | Uygulamanın dağıtık önbellek anahtarlarını ve dil sözlüğünün süreç içi kopyasını temizler (`DistributedCacheCleaner`) | `App.Languages.LanguageText` |
|
||||||
| `UiEvalService.ApiKickUser(userId, gridRef)` | Kullanıcının oturumunu düşürür, grid'i yeniler | — |
|
| `UiEvalService.ApiKickUser(userId, gridRef)` | Kullanıcının oturumunu düşürür, grid'i yeniler | — |
|
||||||
| `UiEvalService.ApiDeleteDynamicService(id, gridRef)` | Dynamic Service kaydını onay alarak siler (yüklü assembly de düşer), grid'i yeniler | `App.DeveloperKit.DynamicServices.Delete` |
|
| `UiEvalService.ApiDeleteDynamicService(id, gridRef)` | Dynamic Service kaydını onay alarak siler (yüklü assembly de düşer), grid'i yeniler | `App.DeveloperKit.DynamicServices.Delete` |
|
||||||
| `UiEvalService.ApiDbMigrate()` | Önce onay sorar (`App.DbMigrate.ConfirmMessage`), sonra migration + seed akışını başlatır, log panelini açar. Kendi onay diyaloğu olan `DbMigrateButton` (SQL Query Manager, Wizard Manager) ikinci parametreyle (`confirmed: true`) bu adımı atlar | `App.Setup.Migrate` |
|
| `UiEvalService.ApiDbMigrate()` | Önce onay sorar (`App.DbMigrate.ConfirmMessage`), sonra migration + seed akışını başlatır, log panelini açar. Kendi onay diyaloğu olan `DbMigrateButton` (SQL Query Manager, Wizard Manager) ikinci parametreyle (`confirmed: true`) bu adımı atlar | `App.Setup.Migrate` |
|
||||||
|
|
@ -4476,6 +4511,7 @@ kullanılacağı** `lowcode-reference.instructions.md` §12'dedir.
|
||||||
| "Dosya yükleme/yönetme" | File Manager | `/admin/files` |
|
| "Dosya yükleme/yönetme" | File Manager | `/admin/files` |
|
||||||
| "Global aramada çıksın" | Global Search kayıtları (`System`, `Group`, `Term`, `Weight`, `Url`) | `/admin/list/App.Definitions.GlobalSearch` |
|
| "Global aramada çıksın" | Global Search kayıtları (`System`, `Group`, `Term`, `Weight`, `Url`) | `/admin/list/App.Definitions.GlobalSearch` |
|
||||||
| "Duyuru, anket, sosyal akış, etkinlik" | İK › Intranet ekranları | `/admin/list/App.Hr.Intranet.*` |
|
| "Duyuru, anket, sosyal akış, etkinlik" | İK › Intranet ekranları | `/admin/list/App.Hr.Intranet.*` |
|
||||||
|
| "Çalışan kendi izin/puantaj/performans/kişisel bilgisini görsün" | Profil sekmeleri (Çalışan Bilgileri · İzinlerim · Puantajım · Performansım) + Intranet panosu widget'ı; persona ekranı menüsüz `App.Hr.My*` wizard'ıdır (§0.12.1) | `/admin/profile/*` · `/admin/intranet` |
|
||||||
| "Para birimi, ülke/il/ilçe, birim, sektör…" | Definitions ekranları | `/admin/list/App.Definitions.*` |
|
| "Para birimi, ülke/il/ilçe, birim, sektör…" | Definitions ekranları | `/admin/list/App.Definitions.*` |
|
||||||
| "Departman, pozisyon/ünvan, yetkinlik, organizasyon şeması" | İK › Definitions · Org Chart (şube bazlı departman/pozisyon) | `/admin/list/App.Hr.*` · `/admin/organization` |
|
| "Departman, pozisyon/ünvan, yetkinlik, organizasyon şeması" | İK › Definitions · Org Chart (şube bazlı departman/pozisyon) | `/admin/list/App.Hr.*` · `/admin/organization` |
|
||||||
| "Eğitim, araç/şoför/araç masrafı, vardiya, olay günlüğü, disiplin dosyası" | İK › Trainings · Vehicles · Shift · Event Log · Disciplinary Case | `/admin/list/App.Hr.{Training,Vehicle,Driver,VehicleExpense,VehicleMovement,Shift,EventLog,DisciplinaryCase}` |
|
| "Eğitim, araç/şoför/araç masrafı, vardiya, olay günlüğü, disiplin dosyası" | İK › Trainings · Vehicles · Shift · Event Log · Disciplinary Case | `/admin/list/App.Hr.{Training,Vehicle,Driver,VehicleExpense,VehicleMovement,Shift,EventLog,DisciplinaryCase}` |
|
||||||
|
|
|
||||||
31
README.md
31
README.md
|
|
@ -249,10 +249,14 @@ kullanıcısı). Uç DbMigrator'ü migrate ile aynı kilit ve SSE akışıyla ç
|
||||||
`PlatformDbMigrationService` yalnızca o tenant'ın şema + seed + `execute` adımlarını yürütür. Loglar
|
`PlatformDbMigrationService` yalnızca o tenant'ın şema + seed + `execute` adımlarını yürütür. Loglar
|
||||||
DB Migrate'teki `DbMigrateLogPanel` ile gösterilir.
|
DB Migrate'teki `DbMigrateLogPanel` ile gösterilir.
|
||||||
|
|
||||||
`configs/seeds` tek klasördür; tenant'a özel bir seed kapsamı yoktur. Tek bir `PlatformDbContext`
|
`configs/seeds` tek klasördür; tenant'a özel bir seed kapsamı yoktur. Kendi veritabanı olan
|
||||||
olduğu için kendi veritabanı olan tenant ekran tanımlarını, menüleri, yetkileri ve wizard
|
tenant'ta ekran tanımları, menüler ve yetki tanımları gibi kiracısız varlıklar host'ta kalır,
|
||||||
tablolarını kendi veritabanından okur. Bu yüzden host ve kendi veritabanı olan her tenant aynı
|
çünkü ABP'nin EF repository'si `IMultiTenant` olmayan varlığı her zaman host bağlantısıyla okur.
|
||||||
dosyalarla kurulur. Host veritabanını paylaşan (bağlantı cümlesi olmayan) tenant'ta dosyalar
|
Kiracının veritabanına iki şey yazılır: `IMultiTenant` varlıklar (kullanıcı, rol, yetki ataması)
|
||||||
|
ve kiracı tablolarının satırları. Bu satırlar `Code = tenant adı` olan `DataSource` üzerinden
|
||||||
|
yazılır (`DataSourceManager`). Tabloların scriptleri ile veri dosyalarının kiracı satırları o
|
||||||
|
veritabanında da gerektiği için host ve kendi veritabanı olan her tenant aynı dosyalarla kurulur.
|
||||||
|
Kiracısız satırlar host'ta zaten olduğundan bu adımda atlanır. Host veritabanını paylaşan (bağlantı cümlesi olmayan) tenant'ta dosyalar
|
||||||
yeniden uygulanmaz, çünkü host seed'i onları aynı veritabanına zaten yazmıştır. Karar
|
yeniden uygulanmaz, çünkü host seed'i onları aynı veritabanına zaten yazmıştır. Karar
|
||||||
`SeedFileScope`'tadır: migrate kararı `DataSeedContext`'e yazar, dosya okuyan her seeder
|
`SeedFileScope`'tadır: migrate kararı `DataSeedContext`'e yazar, dosya okuyan her seeder
|
||||||
(`SqlDataSeeder`, `CrudDataSeeder`, `WizardDataSeeder`, `ListFormSeedDataApplier`, `FinalSeeder`)
|
(`SqlDataSeeder`, `CrudDataSeeder`, `WizardDataSeeder`, `ListFormSeedDataApplier`, `FinalSeeder`)
|
||||||
|
|
@ -630,8 +634,8 @@ paylaşılan dosyalar arşive bir kez, birleşik satırlarla girer ve import ar
|
||||||
| `crud/{entity}.json` | Component'lerin/nesnenin kullandığı CRUD endpoint tanımları |
|
| `crud/{entity}.json` | Component'lerin/nesnenin kullandığı CRUD endpoint tanımları |
|
||||||
| `{sql\|postgres}/[host/]{object\|execute}/{nesne}.sql` | `SelectCommand`'a karşılık gelen SQL nesnesi, lookup sorgularının (`LookupDto.LookupQuery`) `FROM`/`JOIN` ile işaret ettiği nesneler ve Custom yolunda CRUD endpoint'lerinin dayandığı tablolar |
|
| `{sql\|postgres}/[host/]{object\|execute}/{nesne}.sql` | `SelectCommand`'a karşılık gelen SQL nesnesi, lookup sorgularının (`LookupDto.LookupQuery`) `FROM`/`JOIN` ile işaret ettiği nesneler ve Custom yolunda CRUD endpoint'lerinin dayandığı tablolar |
|
||||||
|
|
||||||
Script dosyaları sabit bir sağlayıcı/klasör kombinasyonundan **tahmin edilmez**: kapsam kökündeki
|
Script dosyaları sabit bir sağlayıcı/klasör kombinasyonundan **tahmin edilmez**: seed kökündeki
|
||||||
script klasörleri (`sql/object`, `postgres/execute` …) diskten taranır ve nesne adıyla eşleşen her
|
script klasörleri (`sql/object`, `sql/host/execute`, `postgres/execute` …) diskten taranır ve nesne adıyla eşleşen her
|
||||||
dosya arşive girer. Yeni bir sağlayıcı ya da script klasörü eklendiğinde toplayıcı değişmez.
|
dosya arşive girer. Yeni bir sağlayıcı ya da script klasörü eklendiğinde toplayıcı değişmez.
|
||||||
|
|
||||||
Script bulunamaması **uyarı üretmez**: her tablo seed ile yönetilmez — ABP'nin kendi tabloları
|
Script bulunamaması **uyarı üretmez**: her tablo seed ile yönetilmez — ABP'nin kendi tabloları
|
||||||
|
|
@ -1039,7 +1043,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer
|
||||||
|
|
||||||
| Modül | Kapsam |
|
| Modül | Kapsam |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Sozsoft.Languages** | Dil tanımları, dil anahtarları, çeviri metinleri; veritabanı tabanlı lokalizasyon (`DatabaseResourceLocalizer`). Çeviri okuma (public sayfalar dahil) ABP'nin `application-localization` ucundandır ve yetki istemez; dil/anahtar/metin yönetim uçları ile önbellek temizleme `App.Languages.Language*` / `App.Languages.LanguageText*` yetkilerine bağlıdır (`LanguagesPermissions`). Önbellek temizleme yalnızca uygulamanın anahtarlarını siler (`DistributedCacheCleaner`). |
|
| **Sozsoft.Languages** | Dil tanımları, dil anahtarları, çeviri metinleri; veritabanı tabanlı lokalizasyon (`DatabaseResourceLocalizer`). Çeviri okuma (public sayfalar dahil) ABP'nin `application-localization` ucundandır ve yetki istemez; dil/anahtar/metin yönetim uçları ile önbellek temizleme `App.Languages.Language*` / `App.Languages.LanguageText*` yetkilerine bağlıdır (`LanguagesPermissions`). Önbellek temizleme yalnızca uygulamanın anahtarlarını siler (`DistributedCacheCleaner`). `DatabaseResourceLocalizer` metinleri süreç içinde 30 saniye tutar; ham SQL ile, başka bir API örneğinde ya da DbMigrator'da değişen bir metin en geç 30 saniyede görünür. |
|
||||||
| **Sozsoft.Settings** | Ayar tanımları, sağlayıcılar, şifreleme bayrakları ve ayar ekranı metadata'sı. |
|
| **Sozsoft.Settings** | Ayar tanımları, sağlayıcılar, şifreleme bayrakları ve ayar ekranı metadata'sı. |
|
||||||
| **Sozsoft.Notifications** | Bildirim tipleri, kuralları ve kullanıcı bildirimleri; kanal bazlı yönlendirme. |
|
| **Sozsoft.Notifications** | Bildirim tipleri, kuralları ve kullanıcı bildirimleri; kanal bazlı yönlendirme. |
|
||||||
| **Sozsoft.MailQueue** | Şablon (Scriban) tabanlı gövde üretimi, ek dosya yaşam döngüsü, kuyruk çalıştırma ve günlükleme. |
|
| **Sozsoft.MailQueue** | Şablon (Scriban) tabanlı gövde üretimi, ek dosya yaşam döngüsü, kuyruk çalıştırma ve günlükleme. |
|
||||||
|
|
@ -1072,7 +1076,7 @@ içindedir; oradaki kontroller yalnızca butonları gizler, asıl kontrol AppSer
|
||||||
| Video oda | `/admin/videoroom/*` | Oda listesi, detay, planlama, katılımcı ve ekran paylaşımı. |
|
| Video oda | `/admin/videoroom/*` | Oda listesi, detay, planlama, katılımcı ve ekran paylaşımı. |
|
||||||
| Raporlar | `/admin/reports/:report/view|design/...` | DevExpress rapor görüntüleyici ve tasarımcı. `Raporlar` kategorisindeki şablon: `/admin/reports/DynamicReport/view/{ŞablonId}[/{ListFormCode}?recordId=...]` (§15). |
|
| Raporlar | `/admin/reports/:report/view|design/...` | DevExpress rapor görüntüleyici ve tasarımcı. `Raporlar` kategorisindeki şablon: `/admin/reports/DynamicReport/view/{ŞablonId}[/{ListFormCode}?recordId=...]` (§15). |
|
||||||
| AI asistanı | `/admin/ai` | n8n webhook üzerinden sohbet arayüzü. |
|
| AI asistanı | `/admin/ai` | n8n webhook üzerinden sohbet arayüzü. |
|
||||||
| Profil | `/admin/profile/*` | Genel bilgiler, şifre, bildirim ayarları. |
|
| Profil | `/admin/profile/*` | Genel bilgiler, çalışan bilgileri, izinlerim, puantajım, performansım, şifre, bildirim ayarları. Genel ve Çalışan Bilgileri `api/account/my-profile` ucundan okunur. ABP'nin eşlemesi yalnızca modül uzantısında tanımlı ek alanları taşıdığı için `PlatformProfileAppService` kullanıcının kendi kaydındaki alanları (`ProfileExtraProperties`; sağlık, ücret ve işten çıkış alanları hariç) ve departman/pozisyon/şube/rol adlarını cevaba ekler. Çalışan Bilgileri salt okunurdur. İzin, puantaj ve performans sekmeleri çekirdek, salt okunur bileşenlerdir: `App.Hr.My*` persona ekranlarının satırlarını `list-form-select` ile okuyup kart + tablo olarak gösterir, düzenle/sil çizmez (tek aksiyon izin talebi oluşturma). Sekme, ekranlarından birinin yetkisi yoksa görünmez. |
|
||||||
|
|
||||||
### 10.4. Public site
|
### 10.4. Public site
|
||||||
|
|
||||||
|
|
@ -1309,8 +1313,13 @@ başlığındaki ⓘ simgesi ise ekranın kendi ayrıntılı açıklamasını g
|
||||||
- `PlatformConsts.IsMultiTenant = true`.
|
- `PlatformConsts.IsMultiTenant = true`.
|
||||||
- Tenant çözümleme host adı (subdomain) ve ABP tenant resolver'ları ile yapılır (`utils/subdomain.ts`).
|
- Tenant çözümleme host adı (subdomain) ve ABP tenant resolver'ları ile yapılır (`utils/subdomain.ts`).
|
||||||
- **Tenant başına ayrı veritabanı:** `TenantConnectionString` kayıtları ile her tenant kendi
|
- **Tenant başına ayrı veritabanı:** `TenantConnectionString` kayıtları ile her tenant kendi
|
||||||
veritabanına yönlendirilebilir; migration'lar `DatabaseMigrationEventHandlerBase` üzerinden
|
veritabanına yönlendirilebilir. Bağlantı kaydedildiğinde kiracı adıyla bir `DataSource` açılır
|
||||||
tenant oluşturulduğunda otomatik uygulanır.
|
(`TenantConnectionStringEventHandler`). Şema ve seed DbMigrator ile kurulur: tam migrate
|
||||||
|
bütün kiracıları dolaşır, Tenants ekranının Seed sekmesi yalnızca seçilen kiracıyı kurar
|
||||||
|
(`POST /api/setup/tenants/{id}/seed`, `TenantSeedOptions`). EF ile yalnızca `IMultiTenant`
|
||||||
|
varlıklar kiracı veritabanına gider. ListForm, menü ve yetki tanımı gibi kiracısız varlıkları
|
||||||
|
ABP her zaman host'ta okur. Kiracı tablolarının satırları `DataSourceManager` üzerinden kiracının
|
||||||
|
veri kaynağına gider.
|
||||||
- **Şube (Branch):** Tenant altındaki fiziksel/örgütsel kırılım; kullanıcı–şube eşlemesi
|
- **Şube (Branch):** Tenant altındaki fiziksel/örgütsel kırılım; kullanıcı–şube eşlemesi
|
||||||
`BranchUsers` ile tutulur, lisans limiti girişte kontrol edilir.
|
`BranchUsers` ile tutulur, lisans limiti girişte kontrol edilir.
|
||||||
- **Organizasyon birimi:** ABP `OrganizationUnit` ağacı; ListForm'lar OU bazlı filtrelenebilir.
|
- **Organizasyon birimi:** ABP `OrganizationUnit` ağacı; ListForm'lar OU bazlı filtrelenebilir.
|
||||||
|
|
@ -2159,7 +2168,7 @@ configs/seeds/
|
||||||
| `/api/abp/application-configuration` 503 dönüyor | Veritabanına erişilemiyor. Bağlantı dizesini ve veri konteynerini kontrol edin; `/api/setup/application-status` ile durumu görün. |
|
| `/api/abp/application-configuration` 503 dönüyor | Veritabanına erişilemiyor. Bağlantı dizesini ve veri konteynerini kontrol edin; `/api/setup/application-status` ile durumu görün. |
|
||||||
| UI `/setup` ekranına düşüyor | Şema oluşmamış veya bekleyen migration var. Setup ekranından migration'ı tetikleyin ya da DbMigrator'ı çalıştırın. |
|
| UI `/setup` ekranına düşüyor | Şema oluşmamış veya bekleyen migration var. Setup ekranından migration'ı tetikleyin ya da DbMigrator'ı çalıştırın. |
|
||||||
| Giriş "UserCannotSignIn..." hatası veriyor | [11.3](#113-giriş-engelleri) tablosuna bakın: IP, çalışma saati, doğrulama, şube/eş zamanlı kullanıcı limiti. |
|
| Giriş "UserCannotSignIn..." hatası veriyor | [11.3](#113-giriş-engelleri) tablosuna bakın: IP, çalışma saati, doğrulama, şube/eş zamanlı kullanıcı limiti. |
|
||||||
| Yeni sürüm tarayıcıda görünmüyor | Service worker `prompt` modundadır; güncelleme bildirimini onaylayın veya sekmeyi tamamen kapatıp açın. |
|
| Yeni sürüm tarayıcıda görünmüyor | Yeni worker indirmeyi bitirince kendiliğinden devreye girer ve sayfa yenilenir (`swRegistration.ts`). Kurulum takıldıysa sekmeyi tamamen kapatıp açın ya da DevTools › Application › Service Workers'tan eski worker'ı kaldırın. |
|
||||||
| DevExtreme teması yüklenmiyor | `public/css` altındaki tema dosyaları runtime cache'tedir (StaleWhileRevalidate); hard refresh yeterlidir. |
|
| DevExtreme teması yüklenmiyor | `public/css` altındaki tema dosyaları runtime cache'tedir (StaleWhileRevalidate); hard refresh yeterlidir. |
|
||||||
| Hangfire başlangıçta hata veriyor | Bağlantı dizesi boşsa Hangfire kaydı atlanır ama ABP modülleri yüklenir. Bağlantı dizesini doğrulayın. |
|
| Hangfire başlangıçta hata veriyor | Bağlantı dizesi boşsa Hangfire kaydı atlanır ama ABP modülleri yüklenir. Bağlantı dizesini doğrulayın. |
|
||||||
| Dinamik servis yayınlanmıyor | Önce `TestCompile` çalıştırın; derleme hatası mesajı UI'da döner. `Publish` için ilgili yetki gerekir. |
|
| Dinamik servis yayınlanmıyor | Önce `TestCompile` çalıştırın; derleme hatası mesajı UI'da döner. `Publish` için ilgili yetki gerekir. |
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
@ -6,12 +7,16 @@ using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Sozsoft.Platform.BlobStoring;
|
using Sozsoft.Platform.BlobStoring;
|
||||||
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Extensions;
|
using Sozsoft.Platform.Extensions;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Account;
|
using Volo.Abp.Account;
|
||||||
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Identity;
|
using Volo.Abp.Identity;
|
||||||
using Volo.Abp.Users;
|
using Volo.Abp.Users;
|
||||||
|
using static Sozsoft.Platform.PlatformConsts.AbpIdentity;
|
||||||
using IdentityUser = Volo.Abp.Identity.IdentityUser;
|
using IdentityUser = Volo.Abp.Identity.IdentityUser;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Identity;
|
namespace Sozsoft.Platform.Identity;
|
||||||
|
|
@ -22,27 +27,56 @@ namespace Sozsoft.Platform.Identity;
|
||||||
[Route("/api/account/my-profile")]
|
[Route("/api/account/my-profile")]
|
||||||
public class PlatformProfileAppService : ProfileAppService, IProfileAppService
|
public class PlatformProfileAppService : ProfileAppService, IProfileAppService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Profilin Genel ve Çalışan Bilgileri sekmelerinin gösterdiği ek alanlar. ABP'nin eşlemesi yalnızca
|
||||||
|
/// modül uzantısında tanımlı alanları taşır; bunlar kullanıcıya kendi kaydı olarak elle eklenir.
|
||||||
|
/// Sağlık, ücret ve işten çıkış alanları bilinçli olarak listede yoktur.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly string[] ProfileExtraProperties =
|
||||||
|
[
|
||||||
|
User.HomeAddress, User.Nationality, User.Gender, User.EducationLevel, User.BloodType,
|
||||||
|
User.SecondaryEmail, User.SecondaryPhoneNumber, User.MilitaryStatus, User.MilitaryPostponeEndDate,
|
||||||
|
User.IdentityNumber, User.BirthPlace, User.BirthDate, User.FatherName, User.MotherName,
|
||||||
|
User.MaritalStatus, User.MarriageDate, User.RegistrationNo, User.SskNo, User.PdksCardNo,
|
||||||
|
User.EmploymentType, User.ContractType, User.HireDate, User.ContractEndDate,
|
||||||
|
User.ProbationEndDate, User.SeniorityStartDate, User.DrivingLicenseClass, User.DrivingLicenseNo,
|
||||||
|
User.DrivingLicenseExpiryDate,
|
||||||
|
];
|
||||||
|
|
||||||
public BlobManager _blobCdnManager { get; }
|
public BlobManager _blobCdnManager { get; }
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly IRepository<Department, Guid> _departmentRepository;
|
||||||
|
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
||||||
|
private readonly IRepository<Branch, Guid> _branchRepository;
|
||||||
|
private readonly IRepository<BranchUsers, Guid> _branchUsersRepository;
|
||||||
|
|
||||||
public PlatformProfileAppService(
|
public PlatformProfileAppService(
|
||||||
IdentityUserManager userManager,
|
IdentityUserManager userManager,
|
||||||
IOptions<IdentityOptions> identityOptions,
|
IOptions<IdentityOptions> identityOptions,
|
||||||
BlobManager blobCdnManager,
|
BlobManager blobCdnManager,
|
||||||
IConfiguration configuration
|
IConfiguration configuration,
|
||||||
|
IRepository<Department, Guid> departmentRepository,
|
||||||
|
IRepository<JobPosition, Guid> jobPositionRepository,
|
||||||
|
IRepository<Branch, Guid> branchRepository,
|
||||||
|
IRepository<BranchUsers, Guid> branchUsersRepository
|
||||||
) : base(userManager, identityOptions)
|
) : base(userManager, identityOptions)
|
||||||
{
|
{
|
||||||
_blobCdnManager = blobCdnManager;
|
_blobCdnManager = blobCdnManager;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_departmentRepository = departmentRepository;
|
||||||
|
_jobPositionRepository = jobPositionRepository;
|
||||||
|
_branchRepository = branchRepository;
|
||||||
|
_branchUsersRepository = branchUsersRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RemoteService(false)]
|
[RemoteService(false)]
|
||||||
public override Task<ProfileDto> UpdateAsync(UpdateProfileDto input) => throw new NotImplementedException();
|
public override Task<ProfileDto> UpdateAsync(UpdateProfileDto input) => throw new NotImplementedException();
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public override Task<ProfileDto> GetAsync()
|
public override async Task<ProfileDto> GetAsync()
|
||||||
{
|
{
|
||||||
return base.GetAsync();
|
var user = await UserManager.GetByIdAsync(CurrentUser.GetId());
|
||||||
|
return await MapToProfileAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|
@ -85,7 +119,40 @@ public class PlatformProfileAppService : ProfileAppService, IProfileAppService
|
||||||
await CurrentUnitOfWork.SaveChangesAsync();
|
await CurrentUnitOfWork.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ObjectMapper.Map<IdentityUser, ProfileDto>(user);
|
return await MapToProfileAsync(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Departman, pozisyon ve şube ad olarak eklenir: lookup'ları kullanıcı ekranının
|
||||||
|
/// (<c>AbpIdentity.Users</c>) yetkisiyle okunur, profil ise yetki istemez.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<ProfileDto> MapToProfileAsync(IdentityUser user)
|
||||||
|
{
|
||||||
|
var profile = ObjectMapper.Map<IdentityUser, ProfileDto>(user);
|
||||||
|
|
||||||
|
foreach (var name in ProfileExtraProperties)
|
||||||
|
{
|
||||||
|
profile.ExtraProperties[name] = user.GetProperty(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var departmentId = user.GetDepartmentId();
|
||||||
|
var jobPositionId = user.GetJobPositionId();
|
||||||
|
var branchUsers = await _branchUsersRepository.GetQueryableAsync();
|
||||||
|
var branchIds = await AsyncExecuter.ToListAsync(
|
||||||
|
branchUsers.Where(a => a.UserId == user.Id).Select(a => a.BranchId));
|
||||||
|
|
||||||
|
profile.ExtraProperties["DepartmentName"] = departmentId == Guid.Empty
|
||||||
|
? null
|
||||||
|
: (await _departmentRepository.FindAsync(departmentId))?.Name;
|
||||||
|
profile.ExtraProperties["JobPositionName"] = jobPositionId == Guid.Empty
|
||||||
|
? null
|
||||||
|
: (await _jobPositionRepository.FindAsync(jobPositionId))?.Name;
|
||||||
|
profile.ExtraProperties["Branches"] = (await _branchRepository.GetListAsync(a => branchIds.Contains(a.Id)))
|
||||||
|
.Select(a => a.Name)
|
||||||
|
.ToArray();
|
||||||
|
profile.ExtraProperties["Roles"] = (await UserManager.GetRolesAsync(user)).ToArray();
|
||||||
|
|
||||||
|
return profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,13 @@ Bir çelişki varsa sıra: `ai.instructions.md` → `dotnet.instructions.md` →
|
||||||
karşılık gelen yetkiyle eklenir. `create_table` bu yüzden `.Write`'a ek olarak `App.SqlQueryManager`
|
karşılık gelen yetkiyle eklenir. `create_table` bu yüzden `.Write`'a ek olarak `App.SqlQueryManager`
|
||||||
ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir
|
ister; tablo scriptini tasarımcının `generateCreateTableSql` çıktısıyla aynı biçimde üretir
|
||||||
(`McpTableCreator`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir.
|
(`McpTableCreator`) — tasarımcının şablon kolonları ya da script biçimi değişirse o da değişir.
|
||||||
|
- **Çalışan (persona) ekranları ve profil** → çalışanın/yöneticinin kendi listeleri İK menüsüne
|
||||||
|
konmaz: menüsüz `App.Hr.My*`/`Team*` wizard + Intranet panosu widget'ı. Profil sayfasının İzinlerim /
|
||||||
|
Puantajım / Performansım sekmeleri persona ekranlarını salt okunur gösteren çekirdek bileşenlerdir
|
||||||
|
(düzenle/sil yok, tek aksiyon izin talebi oluşturma); yeni persona
|
||||||
|
ekranını profile eklemek kod + rota değişikliğidir (onay). Çalışan Bilgileri sekmesi ve Genel sekmesi
|
||||||
|
kullanıcının kendi alanlarını `my-profile` ek alanlarından okur (`PlatformProfileAppService.ProfileExtraProperties`);
|
||||||
|
sağlık, ücret ve işten çıkış alanları bu listeye girmez. Ayrıntı: `lowcode.instructions.md` §0.12.1.
|
||||||
- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationTemplate`,
|
- **Bildirim kapsamı** → bildirim tipi, kuralı ve kuyruğu kiracıya aittir (`Adm_T_NotificationTemplate`,
|
||||||
`Adm_T_NotificationType`, `Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`) ve ekranların tamamı
|
`Adm_T_NotificationType`, `Adm_T_NotificationRule`, `Adm_T_Notification`; dördü de `IMultiTenant`) ve ekranların tamamı
|
||||||
Administration › Bildirimler altındadır. Ayrı bir manuel gönderim tablosu yoktur: **Bildirimler**
|
Administration › Bildirimler altındadır. Ayrı bir manuel gönderim tablosu yoktur: **Bildirimler**
|
||||||
|
|
|
||||||
|
|
@ -9790,6 +9790,62 @@
|
||||||
"Key": "Abp.Identity.Profile.General",
|
"Key": "Abp.Identity.Profile.General",
|
||||||
"Value": "Genel"
|
"Value": "Genel"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Id": "193920df-a568-5cbb-8ba2-968b8ce5d56a",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.EmployeeInfo",
|
||||||
|
"Value": "Employee Info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "9327d82b-00d3-58e4-b36e-5e5b9340c20c",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.EmployeeInfo",
|
||||||
|
"Value": "Çalışan Bilgileri"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "9e34f6c5-f535-5681-aa5f-c2c47bb7cf7d",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Leave",
|
||||||
|
"Value": "My Leave"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "e69b4dc5-533e-521a-a11c-6c338b606bd2",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Leave",
|
||||||
|
"Value": "İzinlerim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "e7913140-c7f2-5525-ab1c-78b2c33ecd64",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Timesheet",
|
||||||
|
"Value": "My Timesheet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "d2b0ffe5-04e3-57c7-bee7-e623f091d125",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Timesheet",
|
||||||
|
"Value": "Puantajım"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "a7cc018e-28b1-58c3-b06e-e76b769da6e3",
|
||||||
|
"CultureName": "en",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Performance",
|
||||||
|
"Value": "My Performance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "31d15a57-6cc5-5d60-ad2f-55dbbec62b33",
|
||||||
|
"CultureName": "tr",
|
||||||
|
"ResourceName": "Platform",
|
||||||
|
"Key": "Abp.Identity.Profile.Performance",
|
||||||
|
"Value": "Performansım"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Id": "ad1d44df-623f-55fa-be90-8ad9aca6cd5c",
|
"Id": "ad1d44df-623f-55fa-be90-8ad9aca6cd5c",
|
||||||
"CultureName": "en",
|
"CultureName": "en",
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,82 @@
|
||||||
"SeoPriority": null,
|
"SeoPriority": null,
|
||||||
"SeoStructuredData": null
|
"SeoStructuredData": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Id": "8b7bddc0-21db-5c3f-9964-a3cca0c294ba",
|
||||||
|
"Key": "admin.profile.employeeInfo",
|
||||||
|
"Path": "/admin/profile/employee-info",
|
||||||
|
"ComponentType": "normal",
|
||||||
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
|
"RouteType": "protected",
|
||||||
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "43b30761-b5ed-5f27-8684-d516f7323a7b",
|
||||||
|
"Key": "admin.profile.leave",
|
||||||
|
"Path": "/admin/profile/leave",
|
||||||
|
"ComponentType": "normal",
|
||||||
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
|
"RouteType": "protected",
|
||||||
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "7840151c-13da-5cf3-af54-f7d34e37530c",
|
||||||
|
"Key": "admin.profile.timesheet",
|
||||||
|
"Path": "/admin/profile/timesheet",
|
||||||
|
"ComponentType": "normal",
|
||||||
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
|
"RouteType": "protected",
|
||||||
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "6e9972bc-c8d6-50bf-acab-4c68f2832907",
|
||||||
|
"Key": "admin.profile.performance",
|
||||||
|
"Path": "/admin/profile/performance",
|
||||||
|
"ComponentType": "normal",
|
||||||
|
"ComponentPath": "@/views/admin/profile/Profile",
|
||||||
|
"RouteType": "protected",
|
||||||
|
"Authority": "",
|
||||||
|
"SeoTitle": null,
|
||||||
|
"SeoDescription": null,
|
||||||
|
"SeoKeywords": null,
|
||||||
|
"SeoOgImage": null,
|
||||||
|
"SeoCanonicalUrl": null,
|
||||||
|
"SeoNoIndex": true,
|
||||||
|
"SeoIncludeInSitemap": false,
|
||||||
|
"SeoChangeFrequency": null,
|
||||||
|
"SeoPriority": null,
|
||||||
|
"SeoStructuredData": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Id": "c64d7bd0-e884-55a6-915a-7a43e0c5e684",
|
"Id": "c64d7bd0-e884-55a6-915a-7a43e0c5e684",
|
||||||
"Key": "admin.activityLog",
|
"Key": "admin.activityLog",
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ tek ek vardır (kullanıcı onayıyla): kullanıcı detayındaki **Puantaj** sek
|
||||||
|
|
||||||
| Persona | Ne yapar | Ekran |
|
| Persona | Ne yapar | Ekran |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Çalışan | Seçtiği ayın net saatini, geç kalma/erken çıkış dakikasını, fazla mesaisini, devamsız/izinli/raporlu günlerini ve yıllık toplamı (270 saatlik fazla mesai sınırıyla) görür | Intranet panosu: **Puantajım** widget'ı → Puantajım (aylar) → ayın günleri |
|
| Çalışan | Seçtiği ayın net saatini, geç kalma/erken çıkış dakikasını, fazla mesaisini, devamsız/izinli/raporlu günlerini ve yıllık toplamı (270 saatlik fazla mesai sınırıyla) görür | Intranet panosu: **Puantajım** widget'ı → Puantajım (aylar) → ayın günleri · Profil › Puantajım (aylık puantaj salt okunur) |
|
||||||
| İK | Hareketleri izler/düzeltir, elle takip edilen çalışanın gelmediği günü işaretler, aylık puantajı izler, ayı yeniden hesaplar ve kapatır, çalışanın PDKS Kart No'sunu kullanıcı kartına (İş sekmesi) girer, toleransı ve fazla mesai kurallarını ayarlar | İK › Puantaj ve Devam Takibi · Ayarlar › İnsan Kaynakları › Puantaj · Kullanıcı detayı › Puantaj |
|
| İK | Hareketleri izler/düzeltir, elle takip edilen çalışanın gelmediği günü işaretler, aylık puantajı izler, ayı yeniden hesaplar ve kapatır, çalışanın PDKS Kart No'sunu kullanıcı kartına (İş sekmesi) girer, toleransı ve fazla mesai kurallarını ayarlar | İK › Puantaj ve Devam Takibi · Ayarlar › İnsan Kaynakları › Puantaj · Kullanıcı detayı › Puantaj |
|
||||||
| PDKS (servis kullanıcısı) | Kart okutmalarını toplu gönderir | `POST /api/app/custom-endpoint/hr/attendance/` |
|
| PDKS (servis kullanıcısı) | Kart okutmalarını toplu gönderir | `POST /api/app/custom-endpoint/hr/attendance/` |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ detayındaki **İzinler** sekmesi.
|
||||||
|
|
||||||
| Persona | Ne yapar | Ekran |
|
| Persona | Ne yapar | Ekran |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Çalışan | Bakiyesini izler, izin talebi açar, taleplerinin onay durumunu takip eder, henüz karar verilmemiş talebini düzeltir/siler | Intranet panosu: **İzinlerim** widget'ı → İzin Talebi Oluştur · Taleplerim · Bakiyem |
|
| Çalışan | Bakiyesini izler, izin talebi açar, taleplerinin onay durumunu takip eder, henüz karar verilmemiş talebini düzeltir/siler | Intranet panosu: **İzinlerim** widget'ı → İzin Talebi Oluştur · Taleplerim · Bakiyem · Profil › İzinlerim (bakiye + talepler salt okunur, İzin Talebi Oluştur) |
|
||||||
| Departman müdürü | Departmanının taleplerini onaylar/reddeder | Intranet panosu: **Ekibimin İzinleri** widget'ı → Ekibimin İzin Talepleri |
|
| Departman müdürü | Departmanının taleplerini onaylar/reddeder | Intranet panosu: **Ekibimin İzinleri** widget'ı → Ekibimin İzin Talepleri |
|
||||||
| İK | Tüm talepleri izler, ikinci onayı verir, çalışan adına talep girer, bakiyeleri izler, açılış/devir düzeltmesi girer, çalışma günlerini ve kademeleri ayarlar | İK › İzin Yönetimi › İzin Talepleri · İzin Bakiyeleri · Ayarlar › İnsan Kaynakları › İzin · Kullanıcı detayı › İzinler |
|
| İK | Tüm talepleri izler, ikinci onayı verir, çalışan adına talep girer, bakiyeleri izler, açılış/devir düzeltmesi girer, çalışma günlerini ve kademeleri ayarlar | İK › İzin Yönetimi › İzin Talepleri · İzin Bakiyeleri · Ayarlar › İnsan Kaynakları › İzin · Kullanıcı detayı › İzinler |
|
||||||
| Sağlık birimi | Raporu Sağlık Kartı'na girer; kayıt izin listesine onaylı düşer | İK › Sağlık Kartları › İstirahat Raporları |
|
| Sağlık birimi | Raporu Sağlık Kartı'na girer; kayıt izin listesine onaylı düşer | İK › Sağlık Kartları › İstirahat Raporları |
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Component + SQL nesneleri); yeni entity, migration ya da React kodu yoktur.
|
||||||
| Persona | Ne yapar | Ekran |
|
| Persona | Ne yapar | Ekran |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| İK | Dönem ve yetkinlik seti tanımlar, tüm değerlendirmeleri izler, 360 değerlendiricisi atar, kalibre eder, gelişim planlarını izler | Dönemler · Tüm Değerlendirmeler · Kalibrasyon · Gelişim Planları · Performans Dereceleri |
|
| İK | Dönem ve yetkinlik seti tanımlar, tüm değerlendirmeleri izler, 360 değerlendiricisi atar, kalibre eder, gelişim planlarını izler | Dönemler · Tüm Değerlendirmeler · Kalibrasyon · Gelişim Planları · Performans Dereceleri |
|
||||||
| Çalışan | Hedeflerini girer, gerçekleşeni ve öz değerlendirmesini yazar, yöneticiye gönderir, gelişim aksiyonlarının ilerlemesini günceller | Intranet panosu: **Performansım** widget'ı → Değerlendirmelerim · Gelişim Planım |
|
| Çalışan | Hedeflerini girer, gerçekleşeni ve öz değerlendirmesini yazar, yöneticiye gönderir, gelişim aksiyonlarının ilerlemesini günceller | Intranet panosu: **Performansım** widget'ı → Değerlendirmelerim · Gelişim Planım · Profil › Performansım (Değerlendirmelerim · Hedeflerim · Gelişim Planım · 360 Taleplerim salt okunur) |
|
||||||
| Yönetici | Hedefleri onaylar, yetkinlik seviyesini ve yorumunu verir, kalibrasyona gönderir, gelişim aksiyonu planlar | Intranet panosu: **Ekibimin Performansı** widget'ı → Ekibimin Değerlendirmeleri |
|
| Yönetici | Hedefleri onaylar, yetkinlik seviyesini ve yorumunu verir, kalibrasyona gönderir, gelişim aksiyonu planlar | Intranet panosu: **Ekibimin Performansı** widget'ı → Ekibimin Değerlendirmeleri |
|
||||||
| 360 değerlendiricisi | Kendisine atanan çalışanın yetkinliklerini puanlar ve gönderir | Intranet panosu: **360 Taleplerim** widget'ı → 360 Taleplerim |
|
| 360 değerlendiricisi | Kendisine atanan çalışanın yetkinliklerini puanlar ve gönderir | Intranet panosu: **360 Taleplerim** widget'ı → 360 Taleplerim |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@
|
||||||
general: '/admin/profile/general',
|
general: '/admin/profile/general',
|
||||||
password: '/admin/profile/password',
|
password: '/admin/profile/password',
|
||||||
notificationSettings: '/admin/profile/notification-settings',
|
notificationSettings: '/admin/profile/notification-settings',
|
||||||
|
employeeInfo: '/admin/profile/employee-info',
|
||||||
|
leave: '/admin/profile/leave',
|
||||||
|
timesheet: '/admin/profile/timesheet',
|
||||||
|
performance: '/admin/profile/performance',
|
||||||
},
|
},
|
||||||
activityLog: '/admin/activityLog',
|
activityLog: '/admin/activityLog',
|
||||||
changeLog: '/admin/changeLog',
|
changeLog: '/admin/changeLog',
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,25 @@ import AdaptableCard from '@/components/shared/AdaptableCard'
|
||||||
import Container from '@/components/shared/Container'
|
import Container from '@/components/shared/Container'
|
||||||
import Tabs from '@/components/ui/Tabs'
|
import Tabs from '@/components/ui/Tabs'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { Suspense, lazy, type ReactNode } from 'react'
|
import { Suspense, lazy, type ReactNode } from 'react'
|
||||||
import PageTitle from '@/components/shared/PageTitle'
|
import PageTitle from '@/components/shared/PageTitle'
|
||||||
import { FaCheckCircle, FaCompressAlt, FaUser } from 'react-icons/fa'
|
import {
|
||||||
|
FaCalendarAlt,
|
||||||
|
FaChartLine,
|
||||||
|
FaCheckCircle,
|
||||||
|
FaClock,
|
||||||
|
FaCompressAlt,
|
||||||
|
FaIdCard,
|
||||||
|
FaUser,
|
||||||
|
} from 'react-icons/fa'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
const General = lazy(() => import('./components/General'))
|
const General = lazy(() => import('./components/General'))
|
||||||
|
const MyEmployeeInfo = lazy(() => import('./components/MyEmployeeInfo'))
|
||||||
|
const MyLeave = lazy(() => import('./components/MyLeave'))
|
||||||
|
const MyTimesheet = lazy(() => import('./components/MyTimesheet'))
|
||||||
|
const MyPerformance = lazy(() => import('./components/MyPerformance'))
|
||||||
const Password = lazy(() => import('./components/Password'))
|
const Password = lazy(() => import('./components/Password'))
|
||||||
const NotificationSettings = lazy(() => import('./components/NotificationSettings'))
|
const NotificationSettings = lazy(() => import('./components/NotificationSettings'))
|
||||||
|
|
||||||
|
|
@ -16,12 +29,15 @@ const PROFILE_BASE_PATH = '/admin/profile'
|
||||||
|
|
||||||
const Profile = () => {
|
const Profile = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const settingsMenu: Record<
|
const { checkPermission } = usePermission()
|
||||||
|
const allMenus: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
label: string
|
label: string
|
||||||
path: string
|
path: string
|
||||||
icon?: ReactNode
|
icon?: ReactNode
|
||||||
|
/** Sekme, bu "My" ekranlarından en az birinin yetkisi varsa görünür. */
|
||||||
|
permissions?: string[]
|
||||||
}
|
}
|
||||||
> = {
|
> = {
|
||||||
general: {
|
general: {
|
||||||
|
|
@ -29,6 +45,34 @@ const Profile = () => {
|
||||||
path: 'general',
|
path: 'general',
|
||||||
icon: <FaUser />,
|
icon: <FaUser />,
|
||||||
},
|
},
|
||||||
|
employeeInfo: {
|
||||||
|
label: translate('::Abp.Identity.Profile.EmployeeInfo'),
|
||||||
|
path: 'employee-info',
|
||||||
|
icon: <FaIdCard />,
|
||||||
|
},
|
||||||
|
leave: {
|
||||||
|
label: translate('::Abp.Identity.Profile.Leave'),
|
||||||
|
path: 'leave',
|
||||||
|
icon: <FaCalendarAlt />,
|
||||||
|
permissions: ['App.Hr.MyLeaveRequest', 'App.Hr.MyLeaveBalance'],
|
||||||
|
},
|
||||||
|
timesheet: {
|
||||||
|
label: translate('::Abp.Identity.Profile.Timesheet'),
|
||||||
|
path: 'timesheet',
|
||||||
|
icon: <FaClock />,
|
||||||
|
permissions: ['App.Hr.MyTimesheet'],
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
label: translate('::Abp.Identity.Profile.Performance'),
|
||||||
|
path: 'performance',
|
||||||
|
icon: <FaChartLine />,
|
||||||
|
permissions: [
|
||||||
|
'App.Hr.MyPerformanceReview',
|
||||||
|
'App.Hr.MyPerformanceGoal',
|
||||||
|
'App.Hr.MyDevelopmentPlan',
|
||||||
|
'App.Hr.My360Feedback',
|
||||||
|
],
|
||||||
|
},
|
||||||
password: {
|
password: {
|
||||||
label: translate('::Abp.Identity.Password'),
|
label: translate('::Abp.Identity.Password'),
|
||||||
path: 'password',
|
path: 'password',
|
||||||
|
|
@ -40,6 +84,11 @@ const Profile = () => {
|
||||||
icon: <FaCompressAlt />,
|
icon: <FaCompressAlt />,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
const settingsMenu = Object.fromEntries(
|
||||||
|
Object.entries(allMenus).filter(
|
||||||
|
([, menu]) => !menu.permissions || menu.permissions.some((p) => checkPermission(p)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const path = location.pathname.substring(location.pathname.lastIndexOf('/') + 1)
|
const path = location.pathname.substring(location.pathname.lastIndexOf('/') + 1)
|
||||||
|
|
@ -61,9 +110,7 @@ const Profile = () => {
|
||||||
<TabList>
|
<TabList>
|
||||||
{Object.keys(settingsMenu).map((key) => (
|
{Object.keys(settingsMenu).map((key) => (
|
||||||
<TabNav key={key} value={key}>
|
<TabNav key={key} value={key}>
|
||||||
{settingsMenu[key].icon && (
|
{settingsMenu[key].icon && <span className="mr-2">{settingsMenu[key].icon}</span>}
|
||||||
<span className="mr-2">{settingsMenu[key].icon}</span>
|
|
||||||
)}
|
|
||||||
{settingsMenu[key].label}
|
{settingsMenu[key].label}
|
||||||
</TabNav>
|
</TabNav>
|
||||||
))}
|
))}
|
||||||
|
|
@ -72,6 +119,10 @@ const Profile = () => {
|
||||||
<div className="px-4 py-6">
|
<div className="px-4 py-6">
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
{currentTab === 'general' && <General />}
|
{currentTab === 'general' && <General />}
|
||||||
|
{currentTab === 'employeeInfo' && <MyEmployeeInfo />}
|
||||||
|
{currentTab === 'leave' && <MyLeave />}
|
||||||
|
{currentTab === 'timesheet' && <MyTimesheet />}
|
||||||
|
{currentTab === 'performance' && <MyPerformance />}
|
||||||
{currentTab === 'password' && <Password />}
|
{currentTab === 'password' && <Password />}
|
||||||
{currentTab === 'notificationSettings' && <NotificationSettings />}
|
{currentTab === 'notificationSettings' && <NotificationSettings />}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
|
||||||
210
ui/src/views/admin/profile/components/MyEmployeeInfo.tsx
Normal file
210
ui/src/views/admin/profile/components/MyEmployeeInfo.tsx
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
import { Input } from '@/components/ui'
|
||||||
|
import { FormContainer, FormItem } from '@/components/ui/Form'
|
||||||
|
import { ProfileDto } from '@/proxy/account/models'
|
||||||
|
import { getProfile } from '@/services/account.service'
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { getActiveCulture, LOCALE_DATE_FORMAT, toTwoLetterCulture } from '@/utils/localeFormat'
|
||||||
|
import { maritalStatuses } from '@/views/admin/user-management/userLookups'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import {
|
||||||
|
FaBriefcase,
|
||||||
|
FaCalendarAlt,
|
||||||
|
FaEnvelope,
|
||||||
|
FaHashtag,
|
||||||
|
FaIdCard,
|
||||||
|
FaMapMarkerAlt,
|
||||||
|
FaPhone,
|
||||||
|
FaUserTie,
|
||||||
|
} from 'react-icons/fa'
|
||||||
|
import type { IconType } from 'react-icons'
|
||||||
|
|
||||||
|
type Row = [label: string, value?: string, icon?: IconType]
|
||||||
|
|
||||||
|
const formatDate = (value?: string) =>
|
||||||
|
value
|
||||||
|
? dayjs(value).locale(toTwoLetterCulture(getActiveCulture())).format(LOCALE_DATE_FORMAT)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
/** Genel sekmesiyle aynı düzen: başlık + devre dışı alanlar; çalışan değeri yalnızca görür. */
|
||||||
|
const InfoColumn = ({ title, rows }: { title: string; rows: Row[] }) => (
|
||||||
|
<div>
|
||||||
|
<h6 className="mb-4">{title}</h6>
|
||||||
|
{rows.map(([label, value, Icon]) => (
|
||||||
|
<FormItem key={label} label={label}>
|
||||||
|
<Input
|
||||||
|
disabled
|
||||||
|
type="text"
|
||||||
|
prefix={Icon && <Icon className="text-xl" />}
|
||||||
|
value={value ?? ''}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kullanıcının İK'nın tuttuğu kendi bilgileri (salt okunur). Değerler `my-profile` cevabının ek
|
||||||
|
* alanlarındandır (`PlatformProfileAppService.ProfileExtraProperties`); Genel sekmesinde gösterilen
|
||||||
|
* alanlar burada tekrarlanmaz.
|
||||||
|
*/
|
||||||
|
const MyEmployeeInfo = () => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const [profile, setProfile] = useState<ProfileDto>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let active = true
|
||||||
|
getProfile()
|
||||||
|
.then((response) => active && setProfile(response.data))
|
||||||
|
.catch(() => active && setProfile(undefined))
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!profile) return null
|
||||||
|
|
||||||
|
// Sunucu ek alan adlarını kayıttaki gibi (PascalCase) yazar; serileştirici ayarına karşı iki yazım da okunur.
|
||||||
|
const extra = (key: string) =>
|
||||||
|
profile.extraProperties?.[key] ??
|
||||||
|
profile.extraProperties?.[`${key.charAt(0).toLowerCase()}${key.slice(1)}`]
|
||||||
|
const text = (key: string) => {
|
||||||
|
const value = extra(key)
|
||||||
|
return typeof value === 'string' || typeof value === 'number' ? String(value) : undefined
|
||||||
|
}
|
||||||
|
const list = (key: string) => {
|
||||||
|
const value = extra(key)
|
||||||
|
return Array.isArray(value) ? value.join(', ') : undefined
|
||||||
|
}
|
||||||
|
const info = {
|
||||||
|
departmentName: text('DepartmentName'),
|
||||||
|
jobPositionName: text('JobPositionName'),
|
||||||
|
branches: list('Branches'),
|
||||||
|
roles: list('Roles'),
|
||||||
|
workHour: text('WorkHour'),
|
||||||
|
registrationNo: text('RegistrationNo'),
|
||||||
|
sskNo: text('SskNo'),
|
||||||
|
pdksCardNo: text('PdksCardNo'),
|
||||||
|
employmentType: text('EmploymentType'),
|
||||||
|
contractType: text('ContractType'),
|
||||||
|
hireDate: text('HireDate'),
|
||||||
|
seniorityStartDate: text('SeniorityStartDate'),
|
||||||
|
probationEndDate: text('ProbationEndDate'),
|
||||||
|
contractEndDate: text('ContractEndDate'),
|
||||||
|
secondaryEmail: text('SecondaryEmail'),
|
||||||
|
secondaryPhoneNumber: text('SecondaryPhoneNumber'),
|
||||||
|
identityNumber: text('IdentityNumber'),
|
||||||
|
birthPlace: text('BirthPlace'),
|
||||||
|
birthDate: text('BirthDate'),
|
||||||
|
fatherName: text('FatherName'),
|
||||||
|
motherName: text('MotherName'),
|
||||||
|
maritalStatus: text('MaritalStatus'),
|
||||||
|
marriageDate: text('MarriageDate'),
|
||||||
|
militaryStatus: text('MilitaryStatus'),
|
||||||
|
militaryPostponeEndDate: text('MilitaryPostponeEndDate'),
|
||||||
|
drivingLicenseClass: text('DrivingLicenseClass'),
|
||||||
|
drivingLicenseNo: text('DrivingLicenseNo'),
|
||||||
|
drivingLicenseExpiryDate: text('DrivingLicenseExpiryDate'),
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = (key: string) => translate(`::${key}`)
|
||||||
|
const user = (key: string) => t(`Abp.Identity.User.UserInformation.${key}`)
|
||||||
|
const staticLookup = (value?: string, prefix = '') =>
|
||||||
|
value ? t(`App.StaticLookup.${prefix}${value}`) : undefined
|
||||||
|
const maritalKey = maritalStatuses.find(([value]) => value === info.maritalStatus)?.[1]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormContainer size="md">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 w-full">
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.Organization')}
|
||||||
|
rows={[
|
||||||
|
[t('App.Listform.ListformField.Department'), info.departmentName, FaBriefcase],
|
||||||
|
[user('JobPositionId'), info.jobPositionName, FaUserTie],
|
||||||
|
[t('App.Branches'), info.branches],
|
||||||
|
[t('AbpIdentity.Roles'), info.roles],
|
||||||
|
[t('App.Listform.ListformField.WorkHour'), info.workHour],
|
||||||
|
[user('RegistrationNo'), info.registrationNo, FaHashtag],
|
||||||
|
[user('SskNo'), info.sskNo, FaIdCard],
|
||||||
|
[user('PdksCardNo'), info.pdksCardNo, FaIdCard],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.Contract')}
|
||||||
|
rows={[
|
||||||
|
[user('EmploymentType'), staticLookup(info.employmentType)],
|
||||||
|
[user('ContractType'), staticLookup(info.contractType)],
|
||||||
|
[user('HireDate'), formatDate(info.hireDate), FaCalendarAlt],
|
||||||
|
[user('SeniorityStartDate'), formatDate(info.seniorityStartDate), FaCalendarAlt],
|
||||||
|
[user('ProbationEndDate'), formatDate(info.probationEndDate), FaCalendarAlt],
|
||||||
|
...(info.contractType === 'Definite'
|
||||||
|
? [[user('ContractEndDate'), formatDate(info.contractEndDate), FaCalendarAlt] as Row]
|
||||||
|
: []),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.UserInformation.ContactInformation')}
|
||||||
|
rows={[
|
||||||
|
[user('SecondaryEmail'), info.secondaryEmail, FaEnvelope],
|
||||||
|
[user('SecondaryPhoneNumber'), info.secondaryPhoneNumber, FaPhone],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.IdentityCard')}
|
||||||
|
rows={[
|
||||||
|
[user('IdentityNumber'), info.identityNumber, FaIdCard],
|
||||||
|
[user('BirthPlace'), info.birthPlace, FaMapMarkerAlt],
|
||||||
|
[t('App.Listform.ListformField.BirthDate'), formatDate(info.birthDate), FaCalendarAlt],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.BirthFamily')}
|
||||||
|
rows={[
|
||||||
|
[user('FatherName'), info.fatherName],
|
||||||
|
[user('MotherName'), info.motherName],
|
||||||
|
[
|
||||||
|
t('App.Listform.ListformField.MaritalStatus'),
|
||||||
|
maritalKey ? t(`App.MaritalStatus.${maritalKey}`) : info.maritalStatus,
|
||||||
|
],
|
||||||
|
[user('MarriageDate'), formatDate(info.marriageDate), FaCalendarAlt],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.Military')}
|
||||||
|
rows={[
|
||||||
|
[user('MilitaryStatus'), staticLookup(info.militaryStatus, 'Military')],
|
||||||
|
...(info.militaryStatus === 'Postponed'
|
||||||
|
? [
|
||||||
|
[
|
||||||
|
user('MilitaryPostponeEndDate'),
|
||||||
|
formatDate(info.militaryPostponeEndDate),
|
||||||
|
FaCalendarAlt,
|
||||||
|
] as Row,
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InfoColumn
|
||||||
|
title={t('Abp.Identity.User.Section.DrivingLicense')}
|
||||||
|
rows={[
|
||||||
|
[user('DrivingLicenseClass'), info.drivingLicenseClass?.split('|').join(', ')],
|
||||||
|
[user('DrivingLicenseNo'), info.drivingLicenseNo, FaIdCard],
|
||||||
|
[
|
||||||
|
user('DrivingLicenseExpiryDate'),
|
||||||
|
formatDate(info.drivingLicenseExpiryDate),
|
||||||
|
FaCalendarAlt,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</FormContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyEmployeeInfo
|
||||||
104
ui/src/views/admin/profile/components/MyLeave.tsx
Normal file
104
ui/src/views/admin/profile/components/MyLeave.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
|
import { FaCalendarCheck, FaUmbrellaBeach } from 'react-icons/fa'
|
||||||
|
import { ProfileCard, ProfileLinkButton, ReadOnlyTable, StatusBadge } from './ProfileSections'
|
||||||
|
import { useMyRows } from './profileData'
|
||||||
|
|
||||||
|
const REQUESTS = 'App.Hr.MyLeaveRequest'
|
||||||
|
const BALANCE = 'App.Hr.MyLeaveBalance'
|
||||||
|
|
||||||
|
const formatDays = (value: number) => (Number.isInteger(value) ? String(value) : value.toFixed(1))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* İzinlerim: bakiye ve talepler salt okunur gösterilir. Çalışan yalnızca yeni talep açar; talebin
|
||||||
|
* düzeltilmesi, silinmesi ve kararı onay sürecinde ve İK'dadır (Hr_S_LeaveRequestGuard).
|
||||||
|
*/
|
||||||
|
const MyLeave = () => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const { checkPermission } = usePermission()
|
||||||
|
const requests = useMyRows(REQUESTS)
|
||||||
|
const balance = useMyRows(BALANCE)
|
||||||
|
|
||||||
|
const sum = (field: string) =>
|
||||||
|
balance.rows.reduce((total, row) => total + Number(row[field] ?? 0), 0)
|
||||||
|
const remaining = sum('RemainingDays')
|
||||||
|
const stats: [string, number, string][] = [
|
||||||
|
['EntitledDays', sum('EntitledDays'), 'text-gray-900 dark:text-gray-100'],
|
||||||
|
['UsedDays', sum('UsedDays'), 'text-gray-900 dark:text-gray-100'],
|
||||||
|
['PendingDays', sum('PendingDays'), 'text-orange-500'],
|
||||||
|
['RemainingDays', remaining, remaining < 0 ? 'text-red-600' : 'text-teal-600'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{balance.allowed && (
|
||||||
|
<ProfileCard
|
||||||
|
title={translate(`::${BALANCE}`)}
|
||||||
|
icon={<FaUmbrellaBeach className="h-5 w-5 text-teal-500" />}
|
||||||
|
>
|
||||||
|
<div className="mb-4 grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||||
|
{stats.map(([field, value, color]) => (
|
||||||
|
<div
|
||||||
|
key={field}
|
||||||
|
className="rounded-lg border border-gray-200 bg-gray-50 p-3 text-center dark:border-gray-700 dark:bg-gray-900/40"
|
||||||
|
>
|
||||||
|
<div className={`text-xl font-semibold ${color}`}>{formatDays(value)}</div>
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{translate(`::App.Listform.ListformField.${field}`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<ReadOnlyTable
|
||||||
|
loading={balance.loading}
|
||||||
|
rows={balance.rows}
|
||||||
|
columns={[
|
||||||
|
{ field: 'YearNo' },
|
||||||
|
{ field: 'AccrualDate', date: true },
|
||||||
|
{ field: 'PeriodEndDate', date: true },
|
||||||
|
{ field: 'EntitledDays' },
|
||||||
|
{ field: 'UsedDays' },
|
||||||
|
{ field: 'PendingDays' },
|
||||||
|
{ field: 'RemainingDays' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ProfileCard>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{requests.allowed && (
|
||||||
|
<ProfileCard
|
||||||
|
title={translate(`::${REQUESTS}`)}
|
||||||
|
icon={<FaCalendarCheck className="h-5 w-5 text-teal-500" />}
|
||||||
|
action={
|
||||||
|
checkPermission(`${REQUESTS}.Create`) && (
|
||||||
|
<ProfileLinkButton to={`/admin/form/${REQUESTS}`}>
|
||||||
|
{translate('::App.DashboardWidget.Leave.CreateRequest')}
|
||||||
|
</ProfileLinkButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ReadOnlyTable
|
||||||
|
loading={requests.loading}
|
||||||
|
rows={[...requests.rows].sort((a, b) =>
|
||||||
|
String(b.StartDate).localeCompare(String(a.StartDate)),
|
||||||
|
)}
|
||||||
|
columns={[
|
||||||
|
{ field: 'RequestNo' },
|
||||||
|
{ field: 'StartDate', date: true },
|
||||||
|
{ field: 'EndDate', date: true },
|
||||||
|
{ field: 'Days' },
|
||||||
|
{
|
||||||
|
field: 'ApprovalStatus',
|
||||||
|
render: (row) => <StatusBadge value={row.ApprovalStatus} />,
|
||||||
|
},
|
||||||
|
{ field: 'ApprovalDate', date: true },
|
||||||
|
{ field: 'ApprovalDescription' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ProfileCard>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyLeave
|
||||||
90
ui/src/views/admin/profile/components/MyPerformance.tsx
Normal file
90
ui/src/views/admin/profile/components/MyPerformance.tsx
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
import { FaBullseye, FaChartLine, FaComments, FaSeedling } from 'react-icons/fa'
|
||||||
|
import { ProfileCard, ProfileColumn, ReadOnlyTable, StatusBadge } from './ProfileSections'
|
||||||
|
import { useMyRows } from './profileData'
|
||||||
|
|
||||||
|
const Section = ({
|
||||||
|
listFormCode,
|
||||||
|
icon,
|
||||||
|
columns,
|
||||||
|
}: {
|
||||||
|
listFormCode: string
|
||||||
|
icon: ReactNode
|
||||||
|
columns: ProfileColumn[]
|
||||||
|
}) => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const { allowed, rows, loading } = useMyRows(listFormCode)
|
||||||
|
|
||||||
|
if (!allowed) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProfileCard title={translate(`::${listFormCode}`)} icon={icon}>
|
||||||
|
<ReadOnlyTable loading={loading} rows={rows} columns={columns} />
|
||||||
|
</ProfileCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = (field: string, caption?: string): ProfileColumn => ({
|
||||||
|
field,
|
||||||
|
caption,
|
||||||
|
render: (row) => <StatusBadge value={row[field]} />,
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performansım: değerlendirme, hedef, gelişim planı ve 360 talepleri salt okunur. Öz değerlendirme
|
||||||
|
* ve 360 puanlaması süreç ekranlarında (Intranet panosundaki widget'lardan) yapılır.
|
||||||
|
*/
|
||||||
|
const MyPerformance = () => (
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
<Section
|
||||||
|
listFormCode="App.Hr.MyPerformanceReview"
|
||||||
|
icon={<FaChartLine className="h-5 w-5 text-indigo-500" />}
|
||||||
|
columns={[
|
||||||
|
status('Status', 'ReviewStatus'),
|
||||||
|
{ field: 'GoalScore' },
|
||||||
|
{ field: 'CompetencyScore' },
|
||||||
|
{ field: 'Feedback360Score' },
|
||||||
|
{ field: 'OverallScore' },
|
||||||
|
{ field: 'CalibratedScore' },
|
||||||
|
{ field: 'SelfSubmittedDate', date: true },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Section
|
||||||
|
listFormCode="App.Hr.MyPerformanceGoal"
|
||||||
|
icon={<FaBullseye className="h-5 w-5 text-rose-500" />}
|
||||||
|
columns={[
|
||||||
|
{ field: 'Title', caption: 'GoalTitle' },
|
||||||
|
{ field: 'GoalType' },
|
||||||
|
{ field: 'TargetValue' },
|
||||||
|
{ field: 'ActualValue' },
|
||||||
|
{ field: 'Weight' },
|
||||||
|
{ field: 'AchievementRate' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Section
|
||||||
|
listFormCode="App.Hr.MyDevelopmentPlan"
|
||||||
|
icon={<FaSeedling className="h-5 w-5 text-green-500" />}
|
||||||
|
columns={[
|
||||||
|
{ field: 'Title', caption: 'ActionTitle' },
|
||||||
|
{ field: 'ActionType' },
|
||||||
|
{ field: 'StartDate', date: true },
|
||||||
|
{ field: 'DueDate', date: true },
|
||||||
|
status('Status'),
|
||||||
|
{ field: 'Progress' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Section
|
||||||
|
listFormCode="App.Hr.My360Feedback"
|
||||||
|
icon={<FaComments className="h-5 w-5 text-amber-500" />}
|
||||||
|
columns={[
|
||||||
|
{ field: 'Relation' },
|
||||||
|
status('Status'),
|
||||||
|
{ field: 'Score', caption: 'FeedbackScoreValue' },
|
||||||
|
{ field: 'SubmittedDate', date: true },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default MyPerformance
|
||||||
46
ui/src/views/admin/profile/components/MyTimesheet.tsx
Normal file
46
ui/src/views/admin/profile/components/MyTimesheet.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { FaClock } from 'react-icons/fa'
|
||||||
|
import { ProfileCard, ReadOnlyTable, StatusBadge } from './ProfileSections'
|
||||||
|
import { useMyRows } from './profileData'
|
||||||
|
|
||||||
|
const TIMESHEET = 'App.Hr.MyTimesheet'
|
||||||
|
|
||||||
|
/** Puantajım: aylık puantaj salt okunur; düzeltme İK'nın Puantaj ekranındadır. */
|
||||||
|
const MyTimesheet = () => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const timesheet = useMyRows(TIMESHEET)
|
||||||
|
|
||||||
|
if (!timesheet.allowed) return null
|
||||||
|
|
||||||
|
const rows = [...timesheet.rows].sort(
|
||||||
|
(a, b) =>
|
||||||
|
Number(b.PeriodYear) - Number(a.PeriodYear) || Number(b.PeriodMonth) - Number(a.PeriodMonth),
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProfileCard
|
||||||
|
title={translate(`::${TIMESHEET}`)}
|
||||||
|
icon={<FaClock className="h-5 w-5 text-indigo-500" />}
|
||||||
|
>
|
||||||
|
<ReadOnlyTable
|
||||||
|
loading={timesheet.loading}
|
||||||
|
rows={rows}
|
||||||
|
columns={[
|
||||||
|
{ field: 'PeriodYear' },
|
||||||
|
{ field: 'PeriodMonth' },
|
||||||
|
{ field: 'PeriodStatus', render: (row) => <StatusBadge value={row.PeriodStatus} /> },
|
||||||
|
{ field: 'WorkDays' },
|
||||||
|
{ field: 'NetHours' },
|
||||||
|
{ field: 'LateMinutes' },
|
||||||
|
{ field: 'EarlyLeaveMinutes' },
|
||||||
|
{ field: 'OvertimeHours' },
|
||||||
|
{ field: 'LeaveDays' },
|
||||||
|
{ field: 'SickDays' },
|
||||||
|
{ field: 'HolidayDays' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ProfileCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyTimesheet
|
||||||
114
ui/src/views/admin/profile/components/ProfileSections.tsx
Normal file
114
ui/src/views/admin/profile/components/ProfileSections.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
import Table from '@/components/ui/Table'
|
||||||
|
import TBody from '@/components/ui/Table/TBody'
|
||||||
|
import Td from '@/components/ui/Table/Td'
|
||||||
|
import Th from '@/components/ui/Table/Th'
|
||||||
|
import THead from '@/components/ui/Table/THead'
|
||||||
|
import Tr from '@/components/ui/Table/Tr'
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { formatLocaleValue } from '@/utils/localeFormat'
|
||||||
|
import { formatDay, ProfileRow } from './profileData'
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
/** Intranet panosundaki widget'larla aynı çerçeve: başlık, isteğe bağlı aksiyon, içerik. */
|
||||||
|
export const ProfileCard = ({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
action,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
icon?: ReactNode
|
||||||
|
action?: ReactNode
|
||||||
|
children: ReactNode
|
||||||
|
}) => (
|
||||||
|
<div className="overflow-hidden rounded-xl border border-gray-200/50 bg-white shadow-lg dark:border-gray-700/50 dark:bg-gray-800">
|
||||||
|
<div className="flex items-center justify-between gap-2 border-b border-gray-200 p-4 dark:border-gray-700">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{icon}
|
||||||
|
<h2 className="text-base font-semibold text-gray-900 dark:text-white">{title}</h2>
|
||||||
|
</div>
|
||||||
|
{action}
|
||||||
|
</div>
|
||||||
|
<div className="p-3">{children}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const ProfileLinkButton = ({ to, children }: { to: string; children: ReactNode }) => (
|
||||||
|
<Link
|
||||||
|
to={to}
|
||||||
|
className="rounded-md bg-teal-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-teal-700"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
|
||||||
|
export type ProfileColumn = {
|
||||||
|
field: string
|
||||||
|
/** `App.Listform.ListformField.*` altındaki başlık anahtarı; boşsa alan adı kullanılır. */
|
||||||
|
caption?: string
|
||||||
|
/** Tarih kolonu saat taşımaz; gün olarak gösterilir. */
|
||||||
|
date?: boolean
|
||||||
|
render?: (row: ProfileRow) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatCell = (row: ProfileRow, column: ProfileColumn) => {
|
||||||
|
if (column.render) return column.render(row)
|
||||||
|
const value = column.date ? formatDay(row[column.field]) : formatLocaleValue(row[column.field])
|
||||||
|
return value || '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Salt okunur satır tablosu; düzenleme, silme ve satır komutu yoktur. */
|
||||||
|
export const ReadOnlyTable = ({
|
||||||
|
rows,
|
||||||
|
columns,
|
||||||
|
loading,
|
||||||
|
}: {
|
||||||
|
rows: ProfileRow[]
|
||||||
|
columns: ProfileColumn[]
|
||||||
|
loading?: boolean
|
||||||
|
}) => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
if (loading) return <div className="p-4 text-center text-sm text-gray-500">...</div>
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="p-4 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
{translate('::App.Platform.NoDataFound')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table compact>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<Th key={column.field}>
|
||||||
|
{translate(`::App.Listform.ListformField.${column.caption ?? column.field}`)}
|
||||||
|
</Th>
|
||||||
|
))}
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{rows.map((row, index) => (
|
||||||
|
<Tr key={String(row.Id ?? index)}>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<Td key={column.field}>{formatCell(row, column)}</Td>
|
||||||
|
))}
|
||||||
|
</Tr>
|
||||||
|
))}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StatusBadge = ({ value }: { value: unknown }) =>
|
||||||
|
value ? (
|
||||||
|
<span className="rounded bg-gray-100 px-1.5 py-0.5 text-xs text-gray-700 dark:bg-gray-700 dark:text-gray-200">
|
||||||
|
{String(value)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<>—</>
|
||||||
|
)
|
||||||
42
ui/src/views/admin/profile/components/profileData.ts
Normal file
42
ui/src/views/admin/profile/components/profileData.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import apiService from '@/services/api.service'
|
||||||
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
|
import { getActiveCulture, LOCALE_DATE_FORMAT, toTwoLetterCulture } from '@/utils/localeFormat'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export type ProfileRow = Record<string, unknown>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persona ekranının ("My" ListForm) satırlarını okur. Satır süzmesi (oturumdaki kullanıcı) ve
|
||||||
|
* yetki ListForm'da kalır; profil yalnızca gösterir. Yetki yoksa istek atılmaz.
|
||||||
|
*/
|
||||||
|
export const useMyRows = (listFormCode: string) => {
|
||||||
|
const { checkPermission } = usePermission()
|
||||||
|
const allowed = checkPermission(listFormCode)
|
||||||
|
const [rows, setRows] = useState<ProfileRow[]>([])
|
||||||
|
const [loading, setLoading] = useState(allowed)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!allowed) return
|
||||||
|
let active = true
|
||||||
|
apiService
|
||||||
|
.fetchData<{ data?: ProfileRow[] }>({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/app/list-form-select/select',
|
||||||
|
params: { listFormCode, take: 500 },
|
||||||
|
})
|
||||||
|
.then((response) => active && setRows(response.data?.data ?? []))
|
||||||
|
.catch(() => active && setRows([]))
|
||||||
|
.finally(() => active && setLoading(false))
|
||||||
|
return () => {
|
||||||
|
active = false
|
||||||
|
}
|
||||||
|
}, [allowed, listFormCode])
|
||||||
|
|
||||||
|
return { allowed, rows, loading }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatDay = (value: unknown) =>
|
||||||
|
value
|
||||||
|
? dayjs(String(value)).locale(toTwoLetterCulture(getActiveCulture())).format(LOCALE_DATE_FORMAT)
|
||||||
|
: ''
|
||||||
|
|
@ -110,6 +110,13 @@ import {
|
||||||
SelectField,
|
SelectField,
|
||||||
TextField,
|
TextField,
|
||||||
} from './userFormFields'
|
} from './userFormFields'
|
||||||
|
import {
|
||||||
|
contractTypes,
|
||||||
|
drivingLicenseClasses,
|
||||||
|
employmentTypes,
|
||||||
|
maritalStatuses,
|
||||||
|
militaryStatuses,
|
||||||
|
} from './userLookups'
|
||||||
import { useUserFieldLookup } from './useUserFieldLookup'
|
import { useUserFieldLookup } from './useUserFieldLookup'
|
||||||
|
|
||||||
export interface ClaimTypeDto {
|
export interface ClaimTypeDto {
|
||||||
|
|
@ -117,16 +124,6 @@ export interface ClaimTypeDto {
|
||||||
claimValue: string
|
claimValue: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const militaryStatuses = ['Done', 'Exempt', 'Postponed', 'NotDone']
|
|
||||||
const employmentTypes = ['FullTime', 'PartTime']
|
|
||||||
const contractTypes = ['Indefinite', 'Definite']
|
|
||||||
const drivingLicenseClasses = ['M', 'A1', 'A2', 'A', 'B1', 'B', 'BE', 'C1', 'C1E', 'C', 'CE', 'D1', 'D1E', 'D', 'DE', 'F', 'G']
|
|
||||||
const maritalStatuses = [
|
|
||||||
['Bekar', 'Single'],
|
|
||||||
['Evli', 'Married'],
|
|
||||||
['Boşanmış', 'Divorced'],
|
|
||||||
['Dul', 'Widowed'],
|
|
||||||
] as const
|
|
||||||
|
|
||||||
// Uyruk ülke adıyla saklanır; ad dile göre değişebildiği için Türkiye telefon koduyla tanınır.
|
// Uyruk ülke adıyla saklanır; ad dile göre değişebildiği için Türkiye telefon koduyla tanınır.
|
||||||
const TURKEY_PHONE_CODE = 90
|
const TURKEY_PHONE_CODE = 90
|
||||||
|
|
|
||||||
14
ui/src/views/admin/user-management/userLookups.ts
Normal file
14
ui/src/views/admin/user-management/userLookups.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Kullanıcı kaydındaki sabit seçenekli alanların değerleri. Kullanıcı detayı (düzenleme) ve profilin
|
||||||
|
* Çalışan Bilgileri sekmesi (salt okunur) aynı değer → dil anahtarı eşlemesini kullanır.
|
||||||
|
*/
|
||||||
|
export const militaryStatuses = ['Done', 'Exempt', 'Postponed', 'NotDone']
|
||||||
|
export const employmentTypes = ['FullTime', 'PartTime']
|
||||||
|
export const contractTypes = ['Indefinite', 'Definite']
|
||||||
|
export const drivingLicenseClasses = ['M', 'A1', 'A2', 'A', 'B1', 'B', 'BE', 'C1', 'C1E', 'C', 'CE', 'D1', 'D1E', 'D', 'DE', 'F', 'G']
|
||||||
|
export const maritalStatuses = [
|
||||||
|
['Bekar', 'Single'],
|
||||||
|
['Evli', 'Married'],
|
||||||
|
['Boşanmış', 'Divorced'],
|
||||||
|
['Dul', 'Widowed'],
|
||||||
|
] as const
|
||||||
Loading…
Reference in a new issue