diff --git a/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/GridOptionsDto.cs b/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/GridOptionsDto.cs index 688fe040..09e2b9fe 100644 --- a/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/GridOptionsDto.cs +++ b/api/src/Kurs.Platform.Application.Contracts/ListForms/GridOptionsDto/GridOptionsDto.cs @@ -305,6 +305,9 @@ public class GridOptionsDto : AuditedEntityDto public string ListFormType { get; set; } = ListFormTypeEnum.List; public bool IsSubForm { get; set; } = false; + /// Bu listform show activity gösterilsin mi? + public bool ShowActivity { get; set; } = true; + [JsonIgnore] public string SubFormsJson { get; set; } // Cagrilacak ListFormlar public SubFormDto[] SubFormsDto diff --git a/api/src/Kurs.Platform.Application/ListForms/Administration/ListFormsAppService.cs b/api/src/Kurs.Platform.Application/ListForms/Administration/ListFormsAppService.cs index 42072bf9..951f5519 100644 --- a/api/src/Kurs.Platform.Application/ListForms/Administration/ListFormsAppService.cs +++ b/api/src/Kurs.Platform.Application/ListForms/Administration/ListFormsAppService.cs @@ -64,6 +64,7 @@ public class ListFormsAppService : CrudAppService< item.Height = input.Height; item.Description = input.Description; item.IsSubForm = input.IsSubForm; + item.ShowActivity = input.ShowActivity; item.ListFormType = input.ListFormType; item.LayoutJson = JsonSerializer.Serialize(input.LayoutDto); diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json index 23b7f4fd..9bfc7f86 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/HostData.json @@ -5281,6 +5281,12 @@ "en": "Is Sub", "tr": "Is Sub" }, + { + "resourceName": "Platform", + "key": "ListForms.ListFormEdit.ShowActivity", + "en": "Show Activity", + "tr": "Etkinliği Göster" + }, { "resourceName": "Platform", "key": "ListForms.ListFormEdit.ListFormType", diff --git a/api/src/Kurs.Platform.Domain/Entities/Host/ListForm/ListForm.cs b/api/src/Kurs.Platform.Domain/Entities/Host/ListForm/ListForm.cs index aabda275..c8469584 100644 --- a/api/src/Kurs.Platform.Domain/Entities/Host/ListForm/ListForm.cs +++ b/api/src/Kurs.Platform.Domain/Entities/Host/ListForm/ListForm.cs @@ -115,6 +115,9 @@ public class ListForm : FullAuditedEntity /// Bu listform sub olarak mı kullanılacak public bool IsSubForm { get; set; } + /// Bu listform show activity gösterilsin mi? + public bool ShowActivity { get; set; } + /// Bu listform'un sub listformlarının listesi ve ilişkileri public string SubFormsJson { get; set; } diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.Designer.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.Designer.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.Designer.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.Designer.cs index 3d08fe6f..3be095aa 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.Designer.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Kurs.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20251013062624_Initial")] + [Migration("20251013115205_Initial")] partial class Initial { /// @@ -3841,6 +3841,9 @@ namespace Kurs.Platform.Migrations b.Property("SeriesJson") .HasColumnType("text"); + b.Property("ShowActivity") + .HasColumnType("bit"); + b.Property("SizeJson") .HasColumnType("text"); diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.cs similarity index 99% rename from api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.cs rename to api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.cs index d13ca453..3f5fd729 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013062624_Initial.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/20251013115205_Initial.cs @@ -1628,6 +1628,7 @@ namespace Kurs.Platform.Migrations IsOrganizationUnit = table.Column(type: "bit", nullable: false), ListFormType = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true), IsSubForm = table.Column(type: "bit", nullable: false), + ShowActivity = table.Column(type: "bit", nullable: false), SubFormsJson = table.Column(type: "text", nullable: true), WidgetsJson = table.Column(type: "text", nullable: true), ExtraFilterJson = table.Column(type: "text", nullable: true), diff --git a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index cbc82249..df2ac4ee 100644 --- a/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Kurs.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -3838,6 +3838,9 @@ namespace Kurs.Platform.Migrations b.Property("SeriesJson") .HasColumnType("text"); + b.Property("ShowActivity") + .HasColumnType("bit"); + b.Property("SizeJson") .HasColumnType("text"); diff --git a/ui/src/proxy/form/models.ts b/ui/src/proxy/form/models.ts index 67af7a43..1ccbb4c2 100644 --- a/ui/src/proxy/form/models.ts +++ b/ui/src/proxy/form/models.ts @@ -495,6 +495,7 @@ export interface GridOptionsDto extends AuditedEntityDto { isOrganizationUnit: boolean listFormType: string isSubForm: boolean + showActivity: boolean subFormsJson?: string subFormsDto: SubFormDto[] extraFilterJson?: string diff --git a/ui/src/proxy/formActivity/models.ts b/ui/src/proxy/formActivity/models.ts new file mode 100644 index 00000000..01be436d --- /dev/null +++ b/ui/src/proxy/formActivity/models.ts @@ -0,0 +1,46 @@ +export interface ActivityItem { + id: string + type: 'note' | 'message' + subject: string + content: string + recipientUserName?: string + creatorId: string + creationTime: Date + data?: NoteData | MessageData +} + +export interface NoteData { + id?: string + entityName: string + entityId: string + subject: string + content: string + creatorId?: string + creationTime?: Date + attachedFiles?: FileData[] +} + +export interface FileData { + id?: string + entityName: string + entityId: string + fileName: string + fileSize: number + fileType: string + filePath: string + creatorId?: string + creationTime?: Date +} + +export interface MessageData { + id?: string + entityName: string + entityId: string + recipientUserId: string + recipientUserName: string + subject: string + content: string + creatorId?: string + creationTime?: Date + isRead?: boolean +} \ No newline at end of file diff --git a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx index 6581ecc3..644cb869 100644 --- a/ui/src/views/admin/listForm/edit/FormTabDetails.tsx +++ b/ui/src/views/admin/listForm/edit/FormTabDetails.tsx @@ -15,10 +15,13 @@ import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models' const schema = Yup.object().shape({ cultureName: Yup.string().required('Culture Name Required'), + listFormType: Yup.string().required('List Form Type Required'), title: Yup.string().required('Title Required'), name: Yup.string(), - pageSize: Yup.number(), description: Yup.string(), + pageSize: Yup.number(), + isSubForm: Yup.boolean(), + showActivity: Yup.boolean(), layoutDto: Yup.object().shape({ grid: Yup.boolean(), card: Yup.boolean(), @@ -222,6 +225,18 @@ function FormTabDetails( /> + + + +
void + onDeleteFile?: (fileId: string) => void + onDeleteMessage?: (messageId: string) => void + onDownloadFile?: (fileData: any) => void +} + +export const ActivityList: React.FC = ({ + activities, + onDeleteNote, + onDeleteFile, + onDeleteMessage, + onDownloadFile, +}) => { + const formatDate = (date: Date) => { + return new Intl.DateTimeFormat('tr-TR', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }).format(new Date(date)) + } + + const getActivityIcon = (type: string) => { + switch (type) { + case 'note': + return + case 'message': + return + default: + return + } + } + + const handleDelete = (activity: ActivityItem) => { + switch (activity.type) { + case 'note': + onDeleteNote?.(activity.id) + break + case 'message': + onDeleteMessage?.(activity.id) + break + } + } + + const handleDownloadFile = (fileData: any) => { + onDownloadFile?.(fileData) + } + + if (activities.length === 0) { + return ( +
+ +

Henüz hiçbir aktivite bulunmuyor

+
+ ) + } + + return ( +
+ {activities.map((activity) => ( +
+
+
{getActivityIcon(activity.type)}
+ +
+
+ + {activity.creatorId} + + {activity.recipientUserName && ( + <> + + + {activity.recipientUserName} + + )} +
+ + {activity.subject && ( +

{activity.subject}

+ )} + + {activity.content && ( +

{activity.content}

+ )} + + {/* Note tipinde dosyaları göster */} + {activity.type === 'note' && activity.data && (activity.data as any).attachedFiles?.length > 0 && ( +
+

Ekli Dosyalar:

+
+ {((activity.data as any).attachedFiles || []).map((file: any, index: number) => ( +
+
+ + {file.fileName} + ({(file.fileSize / 1024).toFixed(1)} KB) +
+ +
+ ))} +
+
+ )} + +
+
+ + {formatDate(activity.creationTime)} +
+ +
+ +
+
+
+
+
+ ))} +
+ ) +} diff --git a/ui/src/views/form/FormActivityPanel/ActivityModals.tsx b/ui/src/views/form/FormActivityPanel/ActivityModals.tsx new file mode 100644 index 00000000..14eea8f1 --- /dev/null +++ b/ui/src/views/form/FormActivityPanel/ActivityModals.tsx @@ -0,0 +1,531 @@ +import React, { useState, useEffect } from 'react' +import { Button, Input, Dialog, Select } from '@/components/ui' +import { FaFileUpload, FaEnvelope, FaStickyNote, FaUsers, FaTimes, FaPlus, FaTrash, FaPaperclip } from 'react-icons/fa' +import { FileData } from '@/proxy/formActivity/models' +import { getUsers } from '@/services/identity.service' +import { IdentityUserDto } from '@/proxy/admin/models' + +// Birleştirilmiş Not ve Dosya Ekleme Modal'ı +interface AddContentModalProps { + isOpen: boolean + onClose: () => void + onSaveContent: (subject: string, content: string, files: File[]) => Promise +} + +export const AddContentModal: React.FC = ({ + isOpen, + onClose, + onSaveContent +}) => { + const [subject, setSubject] = useState('') + const [content, setContent] = useState('') + const [selectedFiles, setSelectedFiles] = useState([]) + const [uploading, setUploading] = useState(false) + + const handleFileSelect = (files: FileList | null) => { + if (files && files.length > 0) { + const newFiles = Array.from(files) + setSelectedFiles(prev => [...prev, ...newFiles]) + } + } + + const removeFile = (index: number) => { + setSelectedFiles(prev => prev.filter((_, i) => i !== index)) + } + + const handleSave = async () => { + if (content.trim() || selectedFiles.length > 0) { + setUploading(true) + try { + await onSaveContent(subject.trim(), content.trim(), selectedFiles) + resetForm() + onClose() + } catch (error) { + console.error('Save failed:', error) + } finally { + setUploading(false) + } + } + } + + const resetForm = () => { + setContent('') + setSelectedFiles([]) + setUploading(false) + } + + const handleClose = () => { + resetForm() + onClose() + } + + const isFormValid = content.trim() || selectedFiles.length > 0 + const totalFileSize = selectedFiles.reduce((total, file) => total + file.size, 0) + + return ( + +
+
+

+
+ +
+ Not Ekle +

+
+ +
+
+ + setSubject(e.target.value)} + placeholder="Mesajın konusunu girin..." + className="w-full" + size="sm" + /> +
+ + {/* Not Alanı */} +
+ +