Intranet AppService Announcement
This commit is contained in:
parent
bdf67ec1da
commit
e67b8220ec
13 changed files with 406 additions and 305 deletions
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.Intranet;
|
||||||
|
|
||||||
|
public class AnnouncementDto : FullAuditedEntityDto<Guid>
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
@ -12,5 +12,6 @@ public class IntranetDashboardDto
|
||||||
public List<TrainingDto> Trainings { get; set; } = [];
|
public List<TrainingDto> Trainings { get; set; } = [];
|
||||||
public ExpensesDto Expenses { get; set; } = new ExpensesDto();
|
public ExpensesDto Expenses { get; set; } = new ExpensesDto();
|
||||||
public List<FileItemDto> Documents { get; set; } = [];
|
public List<FileItemDto> Documents { get; set; } = [];
|
||||||
|
public List<AnnouncementDto> Announcements { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
private readonly IRepository<Reservation, Guid> _reservationRepository;
|
private readonly IRepository<Reservation, Guid> _reservationRepository;
|
||||||
private readonly IRepository<Training, Guid> _trainingRepository;
|
private readonly IRepository<Training, Guid> _trainingRepository;
|
||||||
private readonly IRepository<Expense, Guid> _expenseRepository;
|
private readonly IRepository<Expense, Guid> _expenseRepository;
|
||||||
|
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
||||||
|
private readonly IRepository<Department, Guid> _departmentRepository;
|
||||||
|
|
||||||
public IntranetAppService(
|
public IntranetAppService(
|
||||||
ICurrentTenant currentTenant,
|
ICurrentTenant currentTenant,
|
||||||
|
|
@ -39,7 +41,9 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
IRepository<Visitor, Guid> visitorRepository,
|
IRepository<Visitor, Guid> visitorRepository,
|
||||||
IRepository<Reservation, Guid> reservationRepository,
|
IRepository<Reservation, Guid> reservationRepository,
|
||||||
IRepository<Training, Guid> trainingRepository,
|
IRepository<Training, Guid> trainingRepository,
|
||||||
IRepository<Expense, Guid> expenseRepository
|
IRepository<Expense, Guid> expenseRepository,
|
||||||
|
IRepository<Announcement, Guid> announcementRepository,
|
||||||
|
IRepository<Department, Guid> departmentRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_currentTenant = currentTenant;
|
_currentTenant = currentTenant;
|
||||||
|
|
@ -52,6 +56,8 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
_reservationRepository = reservationRepository;
|
_reservationRepository = reservationRepository;
|
||||||
_trainingRepository = trainingRepository;
|
_trainingRepository = trainingRepository;
|
||||||
_expenseRepository = expenseRepository;
|
_expenseRepository = expenseRepository;
|
||||||
|
_announcementRepository = announcementRepository;
|
||||||
|
_departmentRepository = departmentRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
||||||
|
|
@ -64,80 +70,59 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
Reservations = await GetReservationsAsync(),
|
Reservations = await GetReservationsAsync(),
|
||||||
Trainings = await GetTrainingsAsync(),
|
Trainings = await GetTrainingsAsync(),
|
||||||
Expenses = await GetExpensesAsync(),
|
Expenses = await GetExpensesAsync(),
|
||||||
Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet)
|
Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet),
|
||||||
|
Announcements = await GetAnnouncementsAsync()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<FileItemDto>> GetIntranetDocumentsAsync(string folderPath)
|
private async Task<List<AnnouncementDto>> GetAnnouncementsAsync()
|
||||||
{
|
{
|
||||||
var items = new List<FileItemDto>();
|
var announcements = await _announcementRepository
|
||||||
var cdnBasePath = _configuration["App:CdnPath"];
|
.WithDetailsAsync(e => e.Employee)
|
||||||
|
.ContinueWith(t => t.Result.ToList());
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(cdnBasePath))
|
var announcementDtos = new List<AnnouncementDto>();
|
||||||
|
|
||||||
|
// 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");
|
var dto = ObjectMapper.Map<Announcement, AnnouncementDto>(announcement);
|
||||||
return items;
|
|
||||||
|
// Departments string'ini array'e çevir (pipe ile ayrılmış ID'ler)
|
||||||
|
if (!string.IsNullOrEmpty(announcement.Departments))
|
||||||
|
{
|
||||||
|
var departmentIds = announcement.Departments
|
||||||
|
.Split('|', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(d => d.Trim())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
// ID'leri Department Name'lere çevir
|
||||||
|
var departmentNames = new List<string>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tenantId = _currentTenant.Id?.ToString() ?? "host";
|
dto.Departments = departmentNames.ToArray();
|
||||||
var fullPath = Path.Combine(cdnBasePath, tenantId);
|
}
|
||||||
|
else
|
||||||
if (!string.IsNullOrEmpty(folderPath))
|
|
||||||
{
|
{
|
||||||
fullPath = Path.Combine(fullPath, folderPath);
|
dto.Departments = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Directory.Exists(fullPath))
|
announcementDtos.Add(dto);
|
||||||
{
|
|
||||||
Logger.LogWarning($"Directory not found: {fullPath}");
|
|
||||||
return items;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = Directory.GetFiles(fullPath);
|
return announcementDtos;
|
||||||
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"
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ExpensesDto> GetExpensesAsync()
|
private async Task<ExpensesDto> GetExpensesAsync()
|
||||||
|
|
@ -284,4 +269,76 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<FileItemDto>> GetIntranetDocumentsAsync(string folderPath)
|
||||||
|
{
|
||||||
|
var items = new List<FileItemDto>();
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,8 @@ public class IntranetAutoMapperProfile : Profile
|
||||||
CreateMap<Training, TrainingDto>();
|
CreateMap<Training, TrainingDto>();
|
||||||
CreateMap<Currency, CurrencyDto>();
|
CreateMap<Currency, CurrencyDto>();
|
||||||
CreateMap<Expense, ExpenseDto>();
|
CreateMap<Expense, ExpenseDto>();
|
||||||
|
|
||||||
|
CreateMap<Announcement, AnnouncementDto>()
|
||||||
|
.ForMember(dest => dest.Departments, opt => opt.Ignore()); // Manuel olarak set ediliyor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
// Domain/Entities/Announcement.cs
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Volo.Abp.Domain.Entities.Auditing;
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 { mockEmployees } from './mockEmployees'
|
||||||
import {
|
import {
|
||||||
Announcement,
|
|
||||||
Certificate,
|
Certificate,
|
||||||
MealMenu,
|
MealMenu,
|
||||||
ShuttleRoute,
|
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[] = [
|
export const mockShuttleRoutes: ShuttleRoute[] = [
|
||||||
{
|
{
|
||||||
id: 'shuttle1',
|
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[] = [
|
export const mockSurveys: Survey[] = [
|
||||||
{
|
{
|
||||||
id: 'survey1',
|
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' }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@ export interface IntranetDashboardDto {
|
||||||
trainings: TrainingDto[]
|
trainings: TrainingDto[]
|
||||||
expenses: ExpensesDto
|
expenses: ExpensesDto
|
||||||
documents: DocumentDto[]
|
documents: DocumentDto[]
|
||||||
|
announcements: AnnouncementDto[]
|
||||||
|
// surveys: Survey[]
|
||||||
|
// mealMenu: MealMenu[]
|
||||||
|
// shuttleRoutes: ShuttleRoute[]
|
||||||
|
// priorityTasks: TaskDto[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Etkinlik
|
// Etkinlik
|
||||||
|
|
@ -224,3 +229,21 @@ export interface DocumentDto {
|
||||||
isReadOnly: boolean
|
isReadOnly: boolean
|
||||||
childCount: number
|
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 }[]
|
||||||
|
}
|
||||||
|
|
|
||||||
69
ui/src/proxy/intranet/utils.tsx
Normal file
69
ui/src/proxy/intranet/utils.tsx
Normal file
|
|
@ -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 <FaFilePdf className="w-4 h-4 text-red-600 dark:text-red-400" />
|
||||||
|
case '.doc':
|
||||||
|
case '.docx':
|
||||||
|
return <FaFileWord className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||||
|
case '.xls':
|
||||||
|
case '.xlsx':
|
||||||
|
return <FaFileExcel className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||||
|
case '.ppt':
|
||||||
|
case '.pptx':
|
||||||
|
return <FaFilePowerpoint className="w-4 h-4 text-orange-600 dark:text-orange-400" />
|
||||||
|
case '.jpg':
|
||||||
|
case '.jpeg':
|
||||||
|
case '.png':
|
||||||
|
case '.gif':
|
||||||
|
return <FaFileImage className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
||||||
|
case '.zip':
|
||||||
|
case '.rar':
|
||||||
|
return <FaFileArchive className="w-4 h-4 text-yellow-600 dark:text-yellow-400" />
|
||||||
|
case '.txt':
|
||||||
|
return <FaFileCode className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
||||||
|
default:
|
||||||
|
return <FaFileAlt className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,5 @@
|
||||||
import { EmployeeDto } from "@/proxy/intranet/models"
|
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
|
// Görev
|
||||||
export interface Task {
|
export interface Task {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import isBetween from 'dayjs/plugin/isBetween'
|
||||||
import TodayBirthdays from './widgets/TodayBirthdays'
|
import TodayBirthdays from './widgets/TodayBirthdays'
|
||||||
import UpcomingEvents from './widgets/UpcomingEvents'
|
import UpcomingEvents from './widgets/UpcomingEvents'
|
||||||
import RecentDocuments from './widgets/RecentDocuments'
|
import RecentDocuments from './widgets/RecentDocuments'
|
||||||
import ImportantAnnouncements from './widgets/ImportantAnnouncements'
|
|
||||||
import PriorityTasks from './widgets/PriorityTasks'
|
import PriorityTasks from './widgets/PriorityTasks'
|
||||||
import MealWeeklyMenu from './widgets/MealWeeklyMenu'
|
import MealWeeklyMenu from './widgets/MealWeeklyMenu'
|
||||||
import ShuttleSchedule from './widgets/ShuttleSchedule'
|
import ShuttleSchedule from './widgets/ShuttleSchedule'
|
||||||
|
|
@ -31,11 +30,12 @@ import AnnouncementDetailModal from './modals/AnnouncementDetailModal'
|
||||||
|
|
||||||
// Social Wall
|
// Social Wall
|
||||||
import SocialWall from './SocialWall'
|
import SocialWall from './SocialWall'
|
||||||
import { Announcement, Survey, SurveyAnswer } from '@/types/intranet'
|
import { Survey, SurveyAnswer } from '@/types/intranet'
|
||||||
import { Container } from '@/components/shared'
|
import { Container } from '@/components/shared'
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
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 { intranetService } from '@/services/intranet.service'
|
||||||
|
import Announcements from './widgets/Announcements'
|
||||||
|
|
||||||
dayjs.locale('tr')
|
dayjs.locale('tr')
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
@ -45,7 +45,7 @@ const WIDGET_ORDER_KEY = 'dashboard-widget-order'
|
||||||
|
|
||||||
const IntranetDashboard: React.FC = () => {
|
const IntranetDashboard: React.FC = () => {
|
||||||
const { checkPermission } = usePermission()
|
const { checkPermission } = usePermission()
|
||||||
const [selectedAnnouncement, setSelectedAnnouncement] = useState<Announcement | null>(null)
|
const [selectedAnnouncement, setSelectedAnnouncement] = useState<AnnouncementDto | null>(null)
|
||||||
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
|
const [selectedSurvey, setSelectedSurvey] = useState<Survey | null>(null)
|
||||||
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
const [showSurveyModal, setShowSurveyModal] = useState(false)
|
||||||
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
const [showLeaveModal, setShowLeaveModal] = useState(false)
|
||||||
|
|
@ -111,7 +111,7 @@ const IntranetDashboard: React.FC = () => {
|
||||||
const widgetMetadata = [
|
const widgetMetadata = [
|
||||||
{ id: 'upcoming-events', permission: 'App.Intranet.Events.Event.Widget', column: 'left' },
|
{ id: 'upcoming-events', permission: 'App.Intranet.Events.Event.Widget', column: 'left' },
|
||||||
{ id: 'today-birthdays', permission: 'App.Hr.Employee.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: 'upcoming-trainings', permission: 'App.Hr.Training.Widget', column: 'left' },
|
||||||
{ id: 'active-reservations', permission: 'App.Intranet.Reservation.Widget', column: 'left' },
|
{ id: 'active-reservations', permission: 'App.Intranet.Reservation.Widget', column: 'left' },
|
||||||
{ id: 'active-surveys', permission: 'App.Intranet.Survey.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: 'expense-management', permission: 'App.Hr.Expense.Widget', column: 'left' },
|
||||||
{ id: 'social-wall', permission: 'App.Intranet.SocialPost.Widget', column: 'center' },
|
{ id: 'social-wall', permission: 'App.Intranet.SocialPost.Widget', column: 'center' },
|
||||||
{
|
{
|
||||||
id: 'important-announcements',
|
id: 'announcements',
|
||||||
permission: 'App.Intranet.Announcement.Widget',
|
permission: 'App.Intranet.Announcement.Widget',
|
||||||
column: 'right',
|
column: 'right',
|
||||||
},
|
},
|
||||||
|
|
@ -272,15 +272,14 @@ const IntranetDashboard: React.FC = () => {
|
||||||
onNewExpense={() => setShowExpenseModal(true)}
|
onNewExpense={() => setShowExpenseModal(true)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case 'documents':
|
||||||
case 'recent-documents':
|
|
||||||
return <RecentDocuments documents={intranetDashboard?.documents || []} />
|
return <RecentDocuments documents={intranetDashboard?.documents || []} />
|
||||||
|
case 'announcements':
|
||||||
|
return <Announcements announcements={intranetDashboard?.announcements || []} onAnnouncementClick={setSelectedAnnouncement} />
|
||||||
case 'active-surveys':
|
case 'active-surveys':
|
||||||
return <ActiveSurveys onTakeSurvey={handleTakeSurvey} />
|
return <ActiveSurveys onTakeSurvey={handleTakeSurvey} />
|
||||||
case 'social-wall':
|
case 'social-wall':
|
||||||
return <SocialWall />
|
return <SocialWall />
|
||||||
case 'important-announcements':
|
|
||||||
return <ImportantAnnouncements onAnnouncementClick={setSelectedAnnouncement} />
|
|
||||||
case 'priority-tasks':
|
case 'priority-tasks':
|
||||||
return <PriorityTasks />
|
return <PriorityTasks />
|
||||||
case 'meal-weekly-menu':
|
case 'meal-weekly-menu':
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import React from 'react'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { FaTimes, FaEye, FaClipboard } from 'react-icons/fa'
|
import { FaTimes, FaEye, FaClipboard } from 'react-icons/fa'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Announcement } from '@/types/intranet'
|
import { AnnouncementDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface AnnouncementDetailModalProps {
|
interface AnnouncementDetailModalProps {
|
||||||
announcement: Announcement
|
announcement: AnnouncementDto
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,13 +71,13 @@ const AnnouncementDetailModal: React.FC<AnnouncementDetailModalProps> = ({ annou
|
||||||
{/* Author Info */}
|
{/* Author Info */}
|
||||||
<div className="flex items-center gap-3 mt-4">
|
<div className="flex items-center gap-3 mt-4">
|
||||||
<img
|
<img
|
||||||
src={announcement.author.avatar}
|
src={announcement.employee.avatar}
|
||||||
alt={announcement.author.fullName}
|
alt={announcement.employee.fullName}
|
||||||
className="w-12 h-12 rounded-full"
|
className="w-12 h-12 rounded-full"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-semibold text-gray-900 dark:text-white">
|
<p className="font-semibold text-gray-900 dark:text-white">
|
||||||
{announcement.author.fullName}
|
{announcement.employee.fullName}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
|
<div className="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<span>
|
<span>
|
||||||
|
|
@ -154,7 +154,7 @@ const AnnouncementDetailModal: React.FC<AnnouncementDetailModalProps> = ({ annou
|
||||||
Hedef Departmanlar
|
Hedef Departmanlar
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{announcement.departments.map((dept, idx) => (
|
{announcement.departments?.map((dept, idx) => (
|
||||||
<span
|
<span
|
||||||
key={idx}
|
key={idx}
|
||||||
className="px-3 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 text-sm rounded-full"
|
className="px-3 py-1 bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 text-sm rounded-full"
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FaBell, FaEye } from 'react-icons/fa'
|
import { FaBell, FaClipboardCheck, FaEye } from 'react-icons/fa'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mockAnnouncements } from '../../../mocks/mockIntranet'
|
import { AnnouncementDto } from '@/proxy/intranet/models'
|
||||||
import { Announcement } from '@/types/intranet'
|
|
||||||
|
|
||||||
interface ImportantAnnouncementsProps {
|
interface AnnouncementsProps {
|
||||||
onAnnouncementClick: (announcement: Announcement) => void
|
announcements: AnnouncementDto[]
|
||||||
|
onAnnouncementClick: (announcement: AnnouncementDto) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImportantAnnouncements: React.FC<ImportantAnnouncementsProps> = ({ onAnnouncementClick }) => {
|
const Announcements: React.FC<AnnouncementsProps> = ({ announcements, onAnnouncementClick }) => {
|
||||||
const pinnedAnnouncements = mockAnnouncements.filter((a) => a.isPinned).slice(0, 3)
|
const pinnedAnnouncements = announcements.filter((a) => a.isPinned).slice(0, 3)
|
||||||
|
|
||||||
const getCategoryColor = (category: string) => {
|
const getCategoryColor = (category: string) => {
|
||||||
const colors: Record<string, string> = {
|
const colors: Record<string, string> = {
|
||||||
|
|
@ -41,8 +41,8 @@ const ImportantAnnouncements: React.FC<ImportantAnnouncementsProps> = ({ onAnnou
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<img
|
<img
|
||||||
src={announcement.author.avatar}
|
src={announcement.employee.avatar}
|
||||||
alt={announcement.author.fullName}
|
alt={announcement.employee.fullName}
|
||||||
className="w-10 h-10 rounded-full"
|
className="w-10 h-10 rounded-full"
|
||||||
/>
|
/>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
|
@ -53,18 +53,14 @@ const ImportantAnnouncements: React.FC<ImportantAnnouncementsProps> = ({ onAnnou
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-1 text-xs rounded-full ${getCategoryColor(announcement.category)}`}
|
className={`px-2 py-1 text-xs rounded-full ${getCategoryColor(announcement.category)}`}
|
||||||
>
|
>
|
||||||
{announcement.category === 'general' && 'Genel'}
|
{announcement.category}
|
||||||
{announcement.category === 'hr' && 'İK'}
|
|
||||||
{announcement.category === 'it' && 'IT'}
|
|
||||||
{announcement.category === 'event' && 'Etkinlik'}
|
|
||||||
{announcement.category === 'urgent' && 'Acil'}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||||
{announcement.excerpt}
|
{announcement.excerpt}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-4 mt-3 text-xs text-gray-500 dark:text-gray-400">
|
<div className="flex items-center gap-4 mt-3 text-xs text-gray-500 dark:text-gray-400">
|
||||||
<span>{announcement.author.fullName}</span>
|
<span>{announcement.employee.fullName}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{dayjs(announcement.publishDate).fromNow()}</span>
|
<span>{dayjs(announcement.publishDate).fromNow()}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
|
|
@ -77,9 +73,23 @@ const ImportantAnnouncements: React.FC<ImportantAnnouncementsProps> = ({ onAnnou
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{pinnedAnnouncements.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<div className="inline-flex items-center justify-center w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full mb-4">
|
||||||
|
<FaClipboardCheck className="w-8 h-8 text-gray-400" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||||
|
Aktif duyuru bulunmuyor
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Yeni duyurular eklendiğinde burada görünecektir.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImportantAnnouncements
|
export default Announcements
|
||||||
|
|
@ -1,76 +1,8 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { FaFileAlt, FaDownload } from 'react-icons/fa'
|
||||||
FaFileAlt,
|
|
||||||
FaDownload,
|
|
||||||
FaFilePdf,
|
|
||||||
FaFileWord,
|
|
||||||
FaFileExcel,
|
|
||||||
FaFilePowerpoint,
|
|
||||||
FaFileImage,
|
|
||||||
FaFileArchive,
|
|
||||||
FaFileCode,
|
|
||||||
} from 'react-icons/fa'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { DocumentDto } from '@/proxy/intranet/models'
|
import { DocumentDto } from '@/proxy/intranet/models'
|
||||||
|
import { getFileIcon, getFileType } from '@/proxy/intranet/utils'
|
||||||
const getFileIcon = (extension: string) => {
|
|
||||||
switch (extension.toLowerCase()) {
|
|
||||||
case '.pdf':
|
|
||||||
return <FaFilePdf className="w-4 h-4 text-red-600 dark:text-red-400" />
|
|
||||||
case '.doc':
|
|
||||||
case '.docx':
|
|
||||||
return <FaFileWord className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
|
||||||
case '.xls':
|
|
||||||
case '.xlsx':
|
|
||||||
return <FaFileExcel className="w-4 h-4 text-green-600 dark:text-green-400" />
|
|
||||||
case '.ppt':
|
|
||||||
case '.pptx':
|
|
||||||
return <FaFilePowerpoint className="w-4 h-4 text-orange-600 dark:text-orange-400" />
|
|
||||||
case '.jpg':
|
|
||||||
case '.jpeg':
|
|
||||||
case '.png':
|
|
||||||
case '.gif':
|
|
||||||
return <FaFileImage className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
|
||||||
case '.zip':
|
|
||||||
case '.rar':
|
|
||||||
return <FaFileArchive className="w-4 h-4 text-yellow-600 dark:text-yellow-400" />
|
|
||||||
case '.txt':
|
|
||||||
return <FaFileCode className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
|
||||||
default:
|
|
||||||
return <FaFileAlt className="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatFileSize = (bytes: number): string => {
|
const formatFileSize = (bytes: number): string => {
|
||||||
if (bytes === 0) return '0 B'
|
if (bytes === 0) return '0 B'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue