diff --git a/api/src/Kurs.Platform.Application.Contracts/Intranet/AnnouncementDto.cs b/api/src/Kurs.Platform.Application.Contracts/Intranet/AnnouncementDto.cs new file mode 100644 index 00000000..498bdea6 --- /dev/null +++ b/api/src/Kurs.Platform.Application.Contracts/Intranet/AnnouncementDto.cs @@ -0,0 +1,23 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Kurs.Platform.Intranet; + +public class AnnouncementDto : FullAuditedEntityDto +{ + public Guid? TenantId { get; set; } + + public string Title { get; set; } + public string Excerpt { get; set; } + public string Content { get; set; } + public string ImageUrl { get; set; } + public string Category { get; set; } + public Guid? EmployeeId { get; set; } + public EmployeeDto Employee { get; set; } + public DateTime PublishDate { get; set; } + public DateTime? ExpiryDate { get; set; } + public bool IsPinned { get; set; } + public int ViewCount { get; set; } + public string[] Departments { get; set; } + public string Attachments { get; set; } +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.Application.Contracts/Intranet/IntranetDashboardDto.cs b/api/src/Kurs.Platform.Application.Contracts/Intranet/IntranetDashboardDto.cs index c3995b1b..bc6caaca 100644 --- a/api/src/Kurs.Platform.Application.Contracts/Intranet/IntranetDashboardDto.cs +++ b/api/src/Kurs.Platform.Application.Contracts/Intranet/IntranetDashboardDto.cs @@ -12,5 +12,6 @@ public class IntranetDashboardDto public List Trainings { get; set; } = []; public ExpensesDto Expenses { get; set; } = new ExpensesDto(); public List Documents { get; set; } = []; + public List Announcements { get; set; } = []; } diff --git a/api/src/Kurs.Platform.Application/Intranet/IntranetAppService.cs b/api/src/Kurs.Platform.Application/Intranet/IntranetAppService.cs index c2c0abe1..98fd8079 100644 --- a/api/src/Kurs.Platform.Application/Intranet/IntranetAppService.cs +++ b/api/src/Kurs.Platform.Application/Intranet/IntranetAppService.cs @@ -28,6 +28,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService private readonly IRepository _reservationRepository; private readonly IRepository _trainingRepository; private readonly IRepository _expenseRepository; + private readonly IRepository _announcementRepository; + private readonly IRepository _departmentRepository; public IntranetAppService( ICurrentTenant currentTenant, @@ -39,8 +41,10 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService IRepository visitorRepository, IRepository reservationRepository, IRepository trainingRepository, - IRepository expenseRepository - ) + IRepository expenseRepository, + IRepository announcementRepository, + IRepository departmentRepository + ) { _currentTenant = currentTenant; _blobContainer = blobContainer; @@ -52,6 +56,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService _reservationRepository = reservationRepository; _trainingRepository = trainingRepository; _expenseRepository = expenseRepository; + _announcementRepository = announcementRepository; + _departmentRepository = departmentRepository; } public async Task GetIntranetDashboardAsync() @@ -64,80 +70,59 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService Reservations = await GetReservationsAsync(), Trainings = await GetTrainingsAsync(), Expenses = await GetExpensesAsync(), - Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet) + Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet), + Announcements = await GetAnnouncementsAsync() }; } - public async Task> GetIntranetDocumentsAsync(string folderPath) + private async Task> GetAnnouncementsAsync() { - var items = new List(); - var cdnBasePath = _configuration["App:CdnPath"]; + var announcements = await _announcementRepository + .WithDetailsAsync(e => e.Employee) + .ContinueWith(t => t.Result.ToList()); - if (string.IsNullOrEmpty(cdnBasePath)) + var announcementDtos = new List(); + + // Tüm departmanları bir kez çek (performans için) + var allDepartments = await _departmentRepository.GetListAsync(); + + foreach (var announcement in announcements) { - Logger.LogWarning("CDN path is not configured"); - return items; - } + var dto = ObjectMapper.Map(announcement); - var tenantId = _currentTenant.Id?.ToString() ?? "host"; - var fullPath = Path.Combine(cdnBasePath, tenantId); - - if (!string.IsNullOrEmpty(folderPath)) - { - fullPath = Path.Combine(fullPath, folderPath); - } - - if (!Directory.Exists(fullPath)) - { - Logger.LogWarning($"Directory not found: {fullPath}"); - return items; - } - - var files = Directory.GetFiles(fullPath); - foreach (var file in files) - { - var fileInfo = new FileInfo(file); - var relativePath = string.IsNullOrEmpty(folderPath) ? fileInfo.Name : $"{folderPath}/{fileInfo.Name}"; - - items.Add(new FileItemDto + // Departments string'ini array'e çevir (pipe ile ayrılmış ID'ler) + if (!string.IsNullOrEmpty(announcement.Departments)) { - Id = Guid.NewGuid().ToString(), - Name = fileInfo.Name, - Type = "file", - Size = fileInfo.Length, - Extension = fileInfo.Extension, - MimeType = GetMimeType(fileInfo.Extension), - CreatedAt = fileInfo.CreationTime, - ModifiedAt = fileInfo.LastWriteTime, - Path = relativePath, - ParentId = string.Empty, - IsReadOnly = false, - ChildCount = 0 - }); + var departmentIds = announcement.Departments + .Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(d => d.Trim()) + .ToArray(); + + // ID'leri Department Name'lere çevir + var departmentNames = new List(); + foreach (var deptId in departmentIds) + { + if (Guid.TryParse(deptId, out var guid)) + { + var department = allDepartments.FirstOrDefault(d => d.Id == guid); + if (department != null) + { + departmentNames.Add(department.Name); + } + } + } + + dto.Departments = departmentNames.ToArray(); + } + else + { + dto.Departments = []; + } + + announcementDtos.Add(dto); } - return items.OrderBy(x => x.Name).ToList(); - } - - private string GetMimeType(string extension) - { - return extension.ToLowerInvariant() switch - { - ".pdf" => "application/pdf", - ".doc" => "application/msword", - ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - ".xls" => "application/vnd.ms-excel", - ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - ".ppt" => "application/vnd.ms-powerpoint", - ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", - ".jpg" or ".jpeg" => "image/jpeg", - ".png" => "image/png", - ".gif" => "image/gif", - ".txt" => "text/plain", - ".zip" => "application/zip", - ".rar" => "application/x-rar-compressed", - _ => "application/octet-stream" - }; + return announcementDtos; } private async Task GetExpensesAsync() @@ -284,4 +269,76 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService return result; } + + public async Task> GetIntranetDocumentsAsync(string folderPath) + { + var items = new List(); + var cdnBasePath = _configuration["App:CdnPath"]; + + if (string.IsNullOrEmpty(cdnBasePath)) + { + Logger.LogWarning("CDN path is not configured"); + return items; + } + + var tenantId = _currentTenant.Id?.ToString() ?? "host"; + var fullPath = Path.Combine(cdnBasePath, tenantId); + + if (!string.IsNullOrEmpty(folderPath)) + { + fullPath = Path.Combine(fullPath, folderPath); + } + + if (!Directory.Exists(fullPath)) + { + Logger.LogWarning($"Directory not found: {fullPath}"); + return items; + } + + var files = Directory.GetFiles(fullPath); + foreach (var file in files) + { + var fileInfo = new FileInfo(file); + var relativePath = string.IsNullOrEmpty(folderPath) ? fileInfo.Name : $"{folderPath}/{fileInfo.Name}"; + + items.Add(new FileItemDto + { + Id = Guid.NewGuid().ToString(), + Name = fileInfo.Name, + Type = "file", + Size = fileInfo.Length, + Extension = fileInfo.Extension, + MimeType = GetMimeType(fileInfo.Extension), + CreatedAt = fileInfo.CreationTime, + ModifiedAt = fileInfo.LastWriteTime, + Path = relativePath, + ParentId = string.Empty, + IsReadOnly = false, + ChildCount = 0 + }); + } + + return items.OrderBy(x => x.Name).ToList(); + } + + private string GetMimeType(string extension) + { + return extension.ToLowerInvariant() switch + { + ".pdf" => "application/pdf", + ".doc" => "application/msword", + ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + ".xls" => "application/vnd.ms-excel", + ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ".ppt" => "application/vnd.ms-powerpoint", + ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", + ".jpg" or ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".gif" => "image/gif", + ".txt" => "text/plain", + ".zip" => "application/zip", + ".rar" => "application/x-rar-compressed", + _ => "application/octet-stream" + }; + } } diff --git a/api/src/Kurs.Platform.Application/Intranet/IntranetAutoMapperProfile.cs b/api/src/Kurs.Platform.Application/Intranet/IntranetAutoMapperProfile.cs index 0a5bc89c..09c516e9 100644 --- a/api/src/Kurs.Platform.Application/Intranet/IntranetAutoMapperProfile.cs +++ b/api/src/Kurs.Platform.Application/Intranet/IntranetAutoMapperProfile.cs @@ -16,5 +16,8 @@ public class IntranetAutoMapperProfile : Profile CreateMap(); CreateMap(); CreateMap(); + + CreateMap() + .ForMember(dest => dest.Departments, opt => opt.Ignore()); // Manuel olarak set ediliyor } } diff --git a/api/src/Kurs.Platform.Domain/Entities/Tenant/Intranet/Announcement.cs b/api/src/Kurs.Platform.Domain/Entities/Tenant/Intranet/Announcement.cs index 07c5a801..92b8a198 100644 --- a/api/src/Kurs.Platform.Domain/Entities/Tenant/Intranet/Announcement.cs +++ b/api/src/Kurs.Platform.Domain/Entities/Tenant/Intranet/Announcement.cs @@ -1,6 +1,4 @@ -// Domain/Entities/Announcement.cs using System; -using System.Collections.Generic; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; diff --git a/ui/src/mocks/mockIntranet.ts b/ui/src/mocks/mockIntranet.ts index 276a08ec..22258198 100644 --- a/ui/src/mocks/mockIntranet.ts +++ b/ui/src/mocks/mockIntranet.ts @@ -1,7 +1,6 @@ -import { DocumentDto, EventDto, ExpenseDto, ReservationDto, TrainingDto, VisitorDto } from '@/proxy/intranet/models' +import { AnnouncementDto, DocumentDto, EventDto, ExpenseDto, ReservationDto, TrainingDto, VisitorDto } from '@/proxy/intranet/models' import { mockEmployees } from './mockEmployees' import { - Announcement, Certificate, MealMenu, ShuttleRoute, @@ -72,54 +71,6 @@ export const mockMealMenus: MealMenu[] = [ }, ] -export const mockReservations: ReservationDto[] = [ - { - id: 'res1', - type: 'room', - resourceName: 'Toplantı Salonu A', - bookedBy: mockEmployees[2], - startDate: new Date('2024-10-20T09:00:00'), - endDate: new Date('2024-10-20T11:00:00'), - purpose: 'Sprint Planning Toplantısı', - status: 'approved', - participants: 8, - notes: 'Projeksiyon cihazı gerekli', - }, - { - id: 'res2', - type: 'vehicle', - resourceName: 'Şirket Aracı - 34 ABC 123', - bookedBy: mockEmployees[3], - startDate: new Date('2024-10-22T08:00:00'), - endDate: new Date('2024-10-22T18:00:00'), - purpose: 'Müşteri Ziyareti', - status: 'pending', - notes: 'Ankara çıkışı', - }, - { - id: 'res3', - type: 'equipment', - resourceName: 'Kamera ve Tripod Seti', - bookedBy: mockEmployees[5], - startDate: new Date('2024-10-19T14:00:00'), - endDate: new Date('2024-10-19T17:00:00'), - purpose: 'Ürün Tanıtım Videosu Çekimi', - status: 'approved', - }, - { - id: 'res4', - type: 'room', - resourceName: 'Eğitim Salonu B', - bookedBy: mockEmployees[6], - startDate: new Date('2024-10-25T09:00:00'), - endDate: new Date('2024-10-25T17:00:00'), - purpose: 'Etkili İletişim Eğitimi', - status: 'approved', - participants: 15, - notes: 'Tüm gün rezervasyon, öğle yemeği dahil', - }, -] - export const mockShuttleRoutes: ShuttleRoute[] = [ { id: 'shuttle1', @@ -163,74 +114,6 @@ export const mockShuttleRoutes: ShuttleRoute[] = [ }, ] -export const mockAnnouncements: Announcement[] = [ - { - id: 'ann1', - title: '🎉 Yeni Ofis Açılışı', - content: - 'Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.', - excerpt: 'Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!', - category: 'general', - author: mockEmployees[4], - publishDate: new Date('2024-10-15T09:00:00'), - isPinned: true, - viewCount: 156, - imageUrl: 'https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80', - }, - { - id: 'ann2', - title: '📅 Performans Değerlendirme Dönemi', - content: - 'Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.', - excerpt: 'Yıl sonu performans değerlendirmeleri başlıyor.', - category: 'hr', - author: mockEmployees[3], - publishDate: new Date('2024-10-18T10:30:00'), - expiryDate: new Date('2024-11-05'), - isPinned: true, - viewCount: 89, - departments: ['Tüm Departmanlar'], - }, - { - id: 'ann3', - title: '💻 Sistem Bakımı Duyurusu', - content: - 'Bu Cumartesi saat 02:00-06:00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.', - excerpt: 'Cumartesi gecesi planlı bakım çalışması', - category: 'it', - author: mockEmployees[2], - publishDate: new Date('2024-10-17T14:00:00'), - isPinned: false, - viewCount: 234, - }, - { - id: 'ann4', - title: '🎓 React İleri Seviye Eğitimi', - content: - 'Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.', - excerpt: 'React İleri Seviye eğitimi kayıtları başladı', - category: 'event', - author: mockEmployees[0], - publishDate: new Date('2024-10-16T11:00:00'), - isPinned: false, - viewCount: 67, - departments: ['Yazılım Geliştirme'], - }, - { - id: 'ann5', - title: '⚠️ Güvenlik Politikası Güncellemesi', - content: - 'Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.', - excerpt: 'Güvenlik politikası güncellendi - Onay gerekli', - category: 'urgent', - author: mockEmployees[4], - publishDate: new Date('2024-10-18T08:00:00'), - isPinned: true, - viewCount: 312, - attachments: [{ name: 'Bilgi_Guvenligi_Politikasi_v2.pdf', url: '#', size: '2.4 MB' }], - }, -] - export const mockSurveys: Survey[] = [ { id: 'survey1', @@ -1028,3 +911,123 @@ export const mockDocuments: DocumentDto[] = [ }, ] +export const mockReservations: ReservationDto[] = [ + { + id: 'res1', + type: 'room', + resourceName: 'Toplantı Salonu A', + bookedBy: mockEmployees[2], + startDate: new Date('2024-10-20T09:00:00'), + endDate: new Date('2024-10-20T11:00:00'), + purpose: 'Sprint Planning Toplantısı', + status: 'approved', + participants: 8, + notes: 'Projeksiyon cihazı gerekli', + }, + { + id: 'res2', + type: 'vehicle', + resourceName: 'Şirket Aracı - 34 ABC 123', + bookedBy: mockEmployees[3], + startDate: new Date('2024-10-22T08:00:00'), + endDate: new Date('2024-10-22T18:00:00'), + purpose: 'Müşteri Ziyareti', + status: 'pending', + notes: 'Ankara çıkışı', + }, + { + id: 'res3', + type: 'equipment', + resourceName: 'Kamera ve Tripod Seti', + bookedBy: mockEmployees[5], + startDate: new Date('2024-10-19T14:00:00'), + endDate: new Date('2024-10-19T17:00:00'), + purpose: 'Ürün Tanıtım Videosu Çekimi', + status: 'approved', + }, + { + id: 'res4', + type: 'room', + resourceName: 'Eğitim Salonu B', + bookedBy: mockEmployees[6], + startDate: new Date('2024-10-25T09:00:00'), + endDate: new Date('2024-10-25T17:00:00'), + purpose: 'Etkili İletişim Eğitimi', + status: 'approved', + participants: 15, + notes: 'Tüm gün rezervasyon, öğle yemeği dahil', + }, +] + +export const mockAnnouncements: AnnouncementDto[] = [ + { + id: 'ann1', + title: '🎉 Yeni Ofis Açılışı', + content: + 'Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.', + excerpt: 'Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!', + category: 'general', + employeeId: mockEmployees[4].id, + employee: mockEmployees[4], + publishDate: new Date('2024-10-15T09:00:00'), + isPinned: true, + viewCount: 156, + imageUrl: 'https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80', + }, + { + id: 'ann2', + title: '📅 Performans Değerlendirme Dönemi', + content: + 'Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.', + excerpt: 'Yıl sonu performans değerlendirmeleri başlıyor.', + category: 'hr', + employeeId: mockEmployees[3].id, + employee: mockEmployees[3], + publishDate: new Date('2024-10-18T10:30:00'), + expiryDate: new Date('2024-11-05'), + isPinned: true, + viewCount: 89, + departments: ['Tüm Departmanlar'], + }, + { + id: 'ann3', + title: '💻 Sistem Bakımı Duyurusu', + content: + 'Bu Cumartesi saat 02:00-06:00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.', + excerpt: 'Cumartesi gecesi planlı bakım çalışması', + category: 'it', + employeeId: mockEmployees[2].id, + employee: mockEmployees[2], + publishDate: new Date('2024-10-17T14:00:00'), + isPinned: false, + viewCount: 234, + }, + { + id: 'ann4', + title: '🎓 React İleri Seviye Eğitimi', + content: + 'Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.', + excerpt: 'React İleri Seviye eğitimi kayıtları başladı', + category: 'event', + employeeId: mockEmployees[0].id, + employee: mockEmployees[0], + publishDate: new Date('2024-10-16T11:00:00'), + isPinned: false, + viewCount: 67, + departments: ['Yazılım Geliştirme'], + }, + { + id: 'ann5', + title: '⚠️ Güvenlik Politikası Güncellemesi', + content: + 'Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.', + excerpt: 'Güvenlik politikası güncellendi - Onay gerekli', + category: 'urgent', + employeeId: mockEmployees[4].id, + employee: mockEmployees[4], + publishDate: new Date('2024-10-18T08:00:00'), + isPinned: true, + viewCount: 312, + attachments: [{ name: 'Bilgi_Guvenligi_Politikasi_v2.pdf', url: '#', size: '2.4 MB' }], + }, +] diff --git a/ui/src/proxy/intranet/models.ts b/ui/src/proxy/intranet/models.ts index c502379b..08542044 100644 --- a/ui/src/proxy/intranet/models.ts +++ b/ui/src/proxy/intranet/models.ts @@ -22,6 +22,11 @@ export interface IntranetDashboardDto { trainings: TrainingDto[] expenses: ExpensesDto documents: DocumentDto[] + announcements: AnnouncementDto[] + // surveys: Survey[] + // mealMenu: MealMenu[] + // shuttleRoutes: ShuttleRoute[] + // priorityTasks: TaskDto[] } // Etkinlik @@ -224,3 +229,21 @@ export interface DocumentDto { isReadOnly: boolean childCount: number } + +// Duyuru +export interface AnnouncementDto { + id: string + title: string + excerpt: string + content: string + imageUrl?: string + category: string + employeeId: string + employee: EmployeeDto + publishDate: Date + expiryDate?: Date + isPinned: boolean + viewCount: number + departments?: string[] + attachments?: { name: string; url: string; size: string }[] +} diff --git a/ui/src/proxy/intranet/utils.tsx b/ui/src/proxy/intranet/utils.tsx new file mode 100644 index 00000000..32daf9fc --- /dev/null +++ b/ui/src/proxy/intranet/utils.tsx @@ -0,0 +1,69 @@ +import { + FaFileAlt, + FaFilePdf, + FaFileWord, + FaFileExcel, + FaFilePowerpoint, + FaFileImage, + FaFileArchive, + FaFileCode, +} from 'react-icons/fa' + +export const getFileIcon = (extension: string) => { + switch (extension.toLowerCase()) { + case '.pdf': + return + case '.doc': + case '.docx': + return + case '.xls': + case '.xlsx': + return + case '.ppt': + case '.pptx': + return + case '.jpg': + case '.jpeg': + case '.png': + case '.gif': + return + case '.zip': + case '.rar': + return + case '.txt': + return + default: + return + } +} + +export const getFileType = (extension: string) => { + switch (extension.toLowerCase()) { + case '.pdf': + return '📄 PDF' + case '.doc': + case '.docx': + return '📝 Word' + case '.xls': + case '.xlsx': + return '📊 Excel' + case '.ppt': + case '.pptx': + return '📽️ PowerPoint' + case '.jpg': + case '.jpeg': + return '🖼️ JPEG' + case '.png': + return '🖼️ PNG' + case '.gif': + return '🖼️ GIF' + case '.zip': + return '🗜️ ZIP' + case '.rar': + return '🗜️ RAR' + case '.txt': + return '📝 Text' + default: + return '📄 Dosya' + } +} \ No newline at end of file diff --git a/ui/src/types/intranet.ts b/ui/src/types/intranet.ts index 1f7cf31f..30b6f063 100644 --- a/ui/src/types/intranet.ts +++ b/ui/src/types/intranet.ts @@ -1,22 +1,5 @@ import { EmployeeDto } from "@/proxy/intranet/models" -// Duyuru -export interface Announcement { - id: string - title: string - content: string - excerpt: string - category: 'general' | 'hr' | 'it' | 'event' | 'urgent' - author: EmployeeDto - publishDate: Date - expiryDate?: Date - isPinned: boolean - attachments?: { name: string; url: string; size: string }[] - departments?: string[] - viewCount: number - imageUrl?: string -} - // Görev export interface Task { id: string diff --git a/ui/src/views/intranet/Dashboard.tsx b/ui/src/views/intranet/Dashboard.tsx index 5b75e8b7..f7f0657c 100644 --- a/ui/src/views/intranet/Dashboard.tsx +++ b/ui/src/views/intranet/Dashboard.tsx @@ -9,7 +9,6 @@ import isBetween from 'dayjs/plugin/isBetween' import TodayBirthdays from './widgets/TodayBirthdays' import UpcomingEvents from './widgets/UpcomingEvents' import RecentDocuments from './widgets/RecentDocuments' -import ImportantAnnouncements from './widgets/ImportantAnnouncements' import PriorityTasks from './widgets/PriorityTasks' import MealWeeklyMenu from './widgets/MealWeeklyMenu' import ShuttleSchedule from './widgets/ShuttleSchedule' @@ -31,11 +30,12 @@ import AnnouncementDetailModal from './modals/AnnouncementDetailModal' // Social Wall import SocialWall from './SocialWall' -import { Announcement, Survey, SurveyAnswer } from '@/types/intranet' +import { Survey, SurveyAnswer } from '@/types/intranet' import { Container } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' -import { IntranetDashboardDto } from '@/proxy/intranet/models' +import { AnnouncementDto, IntranetDashboardDto } from '@/proxy/intranet/models' import { intranetService } from '@/services/intranet.service' +import Announcements from './widgets/Announcements' dayjs.locale('tr') dayjs.extend(relativeTime) @@ -45,7 +45,7 @@ const WIDGET_ORDER_KEY = 'dashboard-widget-order' const IntranetDashboard: React.FC = () => { const { checkPermission } = usePermission() - const [selectedAnnouncement, setSelectedAnnouncement] = useState(null) + const [selectedAnnouncement, setSelectedAnnouncement] = useState(null) const [selectedSurvey, setSelectedSurvey] = useState(null) const [showSurveyModal, setShowSurveyModal] = useState(false) const [showLeaveModal, setShowLeaveModal] = useState(false) @@ -111,7 +111,7 @@ const IntranetDashboard: React.FC = () => { const widgetMetadata = [ { id: 'upcoming-events', permission: 'App.Intranet.Events.Event.Widget', column: 'left' }, { id: 'today-birthdays', permission: 'App.Hr.Employee.Widget', column: 'left' }, - { id: 'recent-documents', permission: 'App.Files.Widget', column: 'left' }, + { id: 'documents', permission: 'App.Files.Widget', column: 'left' }, { id: 'upcoming-trainings', permission: 'App.Hr.Training.Widget', column: 'left' }, { id: 'active-reservations', permission: 'App.Intranet.Reservation.Widget', column: 'left' }, { id: 'active-surveys', permission: 'App.Intranet.Survey.Widget', column: 'left' }, @@ -119,7 +119,7 @@ const IntranetDashboard: React.FC = () => { { id: 'expense-management', permission: 'App.Hr.Expense.Widget', column: 'left' }, { id: 'social-wall', permission: 'App.Intranet.SocialPost.Widget', column: 'center' }, { - id: 'important-announcements', + id: 'announcements', permission: 'App.Intranet.Announcement.Widget', column: 'right', }, @@ -272,15 +272,14 @@ const IntranetDashboard: React.FC = () => { onNewExpense={() => setShowExpenseModal(true)} /> ) - - case 'recent-documents': + case 'documents': return + case 'announcements': + return case 'active-surveys': return case 'social-wall': return - case 'important-announcements': - return case 'priority-tasks': return case 'meal-weekly-menu': diff --git a/ui/src/views/intranet/modals/AnnouncementDetailModal.tsx b/ui/src/views/intranet/modals/AnnouncementDetailModal.tsx index 6f467cd6..8e9aed1b 100644 --- a/ui/src/views/intranet/modals/AnnouncementDetailModal.tsx +++ b/ui/src/views/intranet/modals/AnnouncementDetailModal.tsx @@ -2,10 +2,10 @@ import React from 'react' import { motion } from 'framer-motion' import { FaTimes, FaEye, FaClipboard } from 'react-icons/fa' import dayjs from 'dayjs' -import { Announcement } from '@/types/intranet' +import { AnnouncementDto } from '@/proxy/intranet/models' interface AnnouncementDetailModalProps { - announcement: Announcement + announcement: AnnouncementDto onClose: () => void } @@ -71,13 +71,13 @@ const AnnouncementDetailModal: React.FC = ({ annou {/* Author Info */}
{announcement.author.fullName}

- {announcement.author.fullName} + {announcement.employee.fullName}

@@ -154,7 +154,7 @@ const AnnouncementDetailModal: React.FC = ({ annou Hedef Departmanlar
- {announcement.departments.map((dept, idx) => ( + {announcement.departments?.map((dept, idx) => ( void +interface AnnouncementsProps { + announcements: AnnouncementDto[] + onAnnouncementClick: (announcement: AnnouncementDto) => void } -const ImportantAnnouncements: React.FC = ({ onAnnouncementClick }) => { - const pinnedAnnouncements = mockAnnouncements.filter((a) => a.isPinned).slice(0, 3) +const Announcements: React.FC = ({ announcements, onAnnouncementClick }) => { + const pinnedAnnouncements = announcements.filter((a) => a.isPinned).slice(0, 3) const getCategoryColor = (category: string) => { const colors: Record = { @@ -41,8 +41,8 @@ const ImportantAnnouncements: React.FC = ({ onAnnou >
{announcement.author.fullName}
@@ -53,18 +53,14 @@ const ImportantAnnouncements: React.FC = ({ onAnnou - {announcement.category === 'general' && 'Genel'} - {announcement.category === 'hr' && 'İK'} - {announcement.category === 'it' && 'IT'} - {announcement.category === 'event' && 'Etkinlik'} - {announcement.category === 'urgent' && 'Acil'} + {announcement.category}

{announcement.excerpt}

- {announcement.author.fullName} + {announcement.employee.fullName} {dayjs(announcement.publishDate).fromNow()} @@ -77,9 +73,23 @@ const ImportantAnnouncements: React.FC = ({ onAnnou
))} + + {pinnedAnnouncements.length === 0 && ( +
+
+ +
+

+ Aktif duyuru bulunmuyor +

+

+ Yeni duyurular eklendiğinde burada görünecektir. +

+
+ )}
) } -export default ImportantAnnouncements +export default Announcements diff --git a/ui/src/views/intranet/widgets/RecentDocuments.tsx b/ui/src/views/intranet/widgets/RecentDocuments.tsx index 77d36fd7..fed3e6c0 100644 --- a/ui/src/views/intranet/widgets/RecentDocuments.tsx +++ b/ui/src/views/intranet/widgets/RecentDocuments.tsx @@ -1,76 +1,8 @@ import React from 'react' -import { - FaFileAlt, - FaDownload, - FaFilePdf, - FaFileWord, - FaFileExcel, - FaFilePowerpoint, - FaFileImage, - FaFileArchive, - FaFileCode, -} from 'react-icons/fa' +import { FaFileAlt, FaDownload } from 'react-icons/fa' import dayjs from 'dayjs' import { DocumentDto } from '@/proxy/intranet/models' - -const getFileIcon = (extension: string) => { - switch (extension.toLowerCase()) { - case '.pdf': - return - case '.doc': - case '.docx': - return - case '.xls': - case '.xlsx': - return - case '.ppt': - case '.pptx': - return - case '.jpg': - case '.jpeg': - case '.png': - case '.gif': - return - case '.zip': - case '.rar': - return - case '.txt': - return - default: - return - } -} - -const getFileType = (extension: string) => { - switch (extension.toLowerCase()) { - case '.pdf': - return '📄 PDF' - case '.doc': - case '.docx': - return '📝 Word' - case '.xls': - case '.xlsx': - return '📊 Excel' - case '.ppt': - case '.pptx': - return '📽️ PowerPoint' - case '.jpg': - case '.jpeg': - return '🖼️ JPEG' - case '.png': - return '🖼️ PNG' - case '.gif': - return '🖼️ GIF' - case '.zip': - return '🗜️ ZIP' - case '.rar': - return '🗜️ RAR' - case '.txt': - return '📝 Text' - default: - return '📄 Dosya' - } -} +import { getFileIcon, getFileType } from '@/proxy/intranet/utils' const formatFileSize = (bytes: number): string => { if (bytes === 0) return '0 B'