From 9ca2b81a50483ef528e3b7b97b5207676c491513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Fri, 15 Aug 2025 18:00:09 +0300 Subject: [PATCH] =?UTF-8?q?Caegory=20se=C3=A7imi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/ReportAppService.cs | 14 +- ui/dev-dist/sw.js | 2 +- ui/src/components/reports/Dashboard.tsx | 283 +++++++++++------- ui/src/proxy/reports/models.ts | 7 + ui/src/services/reports.service.ts | 15 +- ui/src/utils/hooks/useReports.ts | 62 +++- 6 files changed, 253 insertions(+), 130 deletions(-) diff --git a/api/src/Kurs.Platform.Application/Reports/ReportAppService.cs b/api/src/Kurs.Platform.Application/Reports/ReportAppService.cs index a101e339..bdd85733 100644 --- a/api/src/Kurs.Platform.Application/Reports/ReportAppService.cs +++ b/api/src/Kurs.Platform.Application/Reports/ReportAppService.cs @@ -9,6 +9,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; using System.Linq.Dynamic.Core; using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Entities; namespace Kurs.Platform.Reports; @@ -18,15 +19,26 @@ public class ReportAppService : PlatformAppService, IReportAppService private readonly IRepository _reportTemplateRepository; private readonly IRepository _generatedReportRepository; private readonly IRepository _reportParameterRepository; + private readonly IRepository _reportCategotyRepository; public ReportAppService( IRepository reportTemplateRepository, IRepository generatedReportRepository, - IRepository reportParameterRepository) + IRepository reportParameterRepository, + IRepository reportCategotyRepository + ) { _reportTemplateRepository = reportTemplateRepository; _generatedReportRepository = generatedReportRepository; _reportParameterRepository = reportParameterRepository; + _reportCategotyRepository = reportCategotyRepository; + } + + public async Task> GetCategoriesAsync() + { + var entity = await _reportCategotyRepository.GetListAsync(); + + return ObjectMapper.Map, List>(entity); } public async Task> GetTemplatesAsync(GetReportTemplatesInput input) diff --git a/ui/dev-dist/sw.js b/ui/dev-dist/sw.js index ed79e50e..67568d52 100644 --- a/ui/dev-dist/sw.js +++ b/ui/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.e9emm6ltkho" + "revision": "0.71ce98091og" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/ui/src/components/reports/Dashboard.tsx b/ui/src/components/reports/Dashboard.tsx index 2a717517..482d907c 100644 --- a/ui/src/components/reports/Dashboard.tsx +++ b/ui/src/components/reports/Dashboard.tsx @@ -1,28 +1,68 @@ -import React, { useState, useMemo } from 'react' +import React, { useState, useMemo, useEffect } from 'react' import { TemplateEditor } from '../reports/TemplateEditor' import { ReportGenerator } from '../reports/ReportGenerator' import { TemplateCard } from './TemplateCard' import { Button } from '../ui/Button' import { Input } from '../ui/Input' import { Plus, Search, Filter, FileText, BarChart3 } from 'lucide-react' -import { ReportTemplateDto } from '@/proxy/reports/models' +import { ReportCategoryDto, ReportTemplateDto } from '@/proxy/reports/models' import { useReports } from '@/utils/hooks/useReports' +import { useLocalization } from '@/utils/hooks/useLocalization' export const Dashboard: React.FC = () => { - const { templates, isLoading, createTemplate, updateTemplate, deleteTemplate, generateReport } = - useReports() + const { + templates, + categories, + isLoading, + createTemplate, + updateTemplate, + deleteTemplate, + generateReport, + loadTemplatesByCategory, + } = useReports() + + const tumuCategory = useMemo( + () => ({ + id: 'tumu-category', + name: 'Tümü', + description: '', + icon: '📋', + }), + [], + ) const [showEditor, setShowEditor] = useState(false) const [showGenerator, setShowGenerator] = useState(false) const [editingTemplate, setEditingTemplate] = useState(null) const [generatingTemplate, setGeneratingTemplate] = useState(null) const [searchQuery, setSearchQuery] = useState('') - const [selectedCategory, setSelectedCategory] = useState('Tümü') + const [selectedCategory, setSelectedCategory] = useState(null) + const { translate } = useLocalization() - const categories = useMemo(() => { - const cats = ['Tümü', ...new Set(templates.map((t) => t.categoryName))] - return cats - }, [templates]) + // Create category options with "Tümü" at the top + const categoryOptions = useMemo(() => { + const categoryNames = [tumuCategory, ...categories.map((cat) => cat)] + return categoryNames + }, [categories, tumuCategory]) + + // Set default category when component mounts + useEffect(() => { + if (!selectedCategory) { + setSelectedCategory(tumuCategory) + } + }, [tumuCategory, selectedCategory]) + + // Handle category change + useEffect(() => { + if (selectedCategory) { + loadTemplatesByCategory(selectedCategory.name) + } + }, [selectedCategory, loadTemplatesByCategory]) + + // Load templates for default category on mount + useEffect(() => { + loadTemplatesByCategory('Tümü') + }, []) const filteredTemplates = useMemo(() => { return templates.filter((template) => { @@ -31,12 +71,9 @@ export const Dashboard: React.FC = () => { template.description?.toLowerCase().includes(searchQuery.toLowerCase()) || template.tags.some((tag: any) => tag.toLowerCase().includes(searchQuery.toLowerCase())) - const matchesCategory = - selectedCategory === 'Tümü' || template.categoryName === selectedCategory - - return matchesSearch && matchesCategory + return matchesSearch }) - }, [templates, searchQuery, selectedCategory]) + }, [templates, searchQuery]) const handleCreateTemplate = () => { setEditingTemplate(null) @@ -93,7 +130,7 @@ export const Dashboard: React.FC = () => { } return ( -
+
{isLoading ? (
@@ -102,112 +139,138 @@ export const Dashboard: React.FC = () => {
) : ( -
- {/* Stats */} -
-
-
-
-

Toplam Şablon

-

{templates.length}

-
-
- -
-
-
- -
-
-
-

Aktif Kategoriler

-

{categories.length - 1}

-
-
- -
-
-
- -
-
-
-

Toplam Parametre

-

- {templates.reduce((sum, t) => sum + t.parameters.length, 0)} -

-
-
- -
-
-
+
+ {/* Left Sidebar - Categories */} +
+
- {/* Filters */} -
-
-
-
- - setSearchQuery(e.target.value)} - className="pl-10" - /> + {/* Main Content */} +
+

+ {translate('::App.Forum.Dashboard.Statistics')} +

+ {/* Stats */} +
+
+
+
+

Toplam Şablon

+

{templates.length}

+
+
+ +
-
- +
+
+
+

Aktif Kategoriler

+

{categories.length}

+
+
+ +
+
+
+ +
+
+
+

Toplam Parametre

+

+ {templates.reduce((sum, t) => sum + t.parameters.length, 0)} +

+
+
+ +
+
-
- - {/* Templates Grid */} - {filteredTemplates.length === 0 ? ( -
- -

- {templates.length === 0 ? 'Henüz şablon oluşturulmamış' : 'Şablon bulunamadı'} -

-

- {templates.length === 0 - ? 'İlk rapor şablonunuzu oluşturarak başlayın.' - : 'Arama kriterlerinize uygun şablon bulunamadı.'} -

- {templates.length === 0 && ( + {/* Filters */} +
+
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10" + /> +
+
- )} +
- ) : ( -
- {filteredTemplates.map((template) => ( - - ))} -
- )} -
+ {/* Templates Grid */} + {filteredTemplates.length === 0 ? ( +
+ +

+ {templates.length === 0 ? 'Henüz şablon oluşturulmamış' : 'Şablon bulunamadı'} +

+

+ {templates.length === 0 + ? 'İlk rapor şablonunuzu oluşturarak başlayın.' + : 'Arama kriterlerinize uygun şablon bulunamadı.'} +

+ {templates.length === 0 && ( + + )} +
+ ) : ( +
+ {filteredTemplates.map((template) => ( + + ))} +
+ )} +
+
)} {/* Modals */} diff --git a/ui/src/proxy/reports/models.ts b/ui/src/proxy/reports/models.ts index 7cf87368..85ab2b3d 100644 --- a/ui/src/proxy/reports/models.ts +++ b/ui/src/proxy/reports/models.ts @@ -1,5 +1,12 @@ export type ReportParameterType = 'text' | 'number' | 'date' | 'select' | 'checkbox' +export interface ReportCategoryDto { + id: string + name: string + description?: string + icon?: string +} + export interface ReportParameterDto { id: string reportTemplateId: string diff --git a/ui/src/services/reports.service.ts b/ui/src/services/reports.service.ts index aa9d2cbc..3f676dfb 100644 --- a/ui/src/services/reports.service.ts +++ b/ui/src/services/reports.service.ts @@ -3,7 +3,9 @@ import { ReportGeneratedDto, CreateReportTemplateDto, UpdateReportTemplateDto, - ReportGenerateDto, // backend'deki GenerateReportDto (templateId + parameters) + ReportGenerateDto, + ReportCategoryDto, + GetReportTemplatesInput, // backend'deki GenerateReportDto (templateId + parameters) } from '@/proxy/reports/models' import apiService from './api.service' import { PagedAndSortedResultRequestDto, PagedResultDto } from '@/proxy' @@ -16,8 +18,17 @@ export interface ReportsData { export class ReportsService { apiName = 'Default' + getCategories = () => + apiService.fetchData( + { + method: 'GET', + url: '/api/app/report/categories', + }, + { apiName: this.apiName }, + ) + // TEMPLATES - getTemplates = (input: PagedAndSortedResultRequestDto) => + getTemplates = (input: GetReportTemplatesInput) => apiService.fetchData, PagedAndSortedResultRequestDto>( { method: 'GET', diff --git a/ui/src/utils/hooks/useReports.ts b/ui/src/utils/hooks/useReports.ts index 02cb952f..d6cfc6d8 100644 --- a/ui/src/utils/hooks/useReports.ts +++ b/ui/src/utils/hooks/useReports.ts @@ -1,4 +1,4 @@ -import { ReportGeneratedDto, ReportTemplateDto } from '@/proxy/reports/models' +import { ReportGeneratedDto, ReportTemplateDto, ReportCategoryDto } from '@/proxy/reports/models' import ReportsService from '@/services/reports.service' import { useState, useCallback, useEffect } from 'react' @@ -7,12 +7,14 @@ const reportsService = new ReportsService() interface ReportData { templates: ReportTemplateDto[] generatedReports: ReportGeneratedDto[] + categories: ReportCategoryDto[] } export const useReports = () => { const [data, setData] = useState({ templates: [], generatedReports: [], + categories: [], }) const [isLoading, setIsLoading] = useState(false) @@ -22,22 +24,26 @@ export const useReports = () => { try { await new Promise((resolve) => setTimeout(resolve, 200)) - const [templatesResponse, generatedReportsResponse] = await Promise.all([ - reportsService.getTemplates({ - sorting: '', - skipCount: 0, - maxResultCount: 1000, - }), - reportsService.getGeneratedReports({ - sorting: '', - skipCount: 0, - maxResultCount: 1000, - }), - ]) + const [templatesResponse, generatedReportsResponse, categoriesResponse] = await Promise.all( + [ + reportsService.getTemplates({ + sorting: '', + skipCount: 0, + maxResultCount: 1000, + }), + reportsService.getGeneratedReports({ + sorting: '', + skipCount: 0, + maxResultCount: 1000, + }), + reportsService.getCategories(), + ], + ) setData({ templates: templatesResponse.data.items || [], generatedReports: generatedReportsResponse.data.items || [], + categories: categoriesResponse.data || [], }) } catch (error) { console.error('Error loading data:', error) @@ -45,6 +51,7 @@ export const useReports = () => { setData({ templates: [], generatedReports: [], + categories: [], }) } finally { setIsLoading(false) @@ -93,7 +100,7 @@ export const useReports = () => { ...prevData, templates: prevData.templates.map((template) => template.id === id - ? { ...template, ...updates, lastModificationTime: new Date() } + ? { ...template, ...updates, lastModificationTime: new Date().toISOString() } : template, ), })) @@ -127,10 +134,10 @@ export const useReports = () => { async (templateId: string, parameterValues: Record) => { setIsLoading(true) try { - const reportData: ReportGeneratedDto = { + const reportData = { templateId, parameters: parameterValues, - } as ReportGeneratedDto + } const response = await reportsService.generateReport(reportData) const report = response.data as ReportGeneratedDto @@ -182,9 +189,31 @@ export const useReports = () => { [data.templates], ) + const loadTemplatesByCategory = useCallback(async (categoryName?: string) => { + setIsLoading(true) + try { + const templatesResponse = await reportsService.getTemplates({ + sorting: '', + skipCount: 0, + maxResultCount: 1000, + category: categoryName && categoryName !== 'Tümü' ? categoryName : undefined, + }) + + setData((prevData) => ({ + ...prevData, + templates: templatesResponse.data.items || [], + })) + } catch (error) { + console.error('Error loading templates by category:', error) + } finally { + setIsLoading(false) + } + }, []) + return { templates: data.templates, generatedReports: data.generatedReports, + categories: data.categories, isLoading, setIsLoading, createTemplate, @@ -193,5 +222,6 @@ export const useReports = () => { generateReport, getReportById, getTemplateById, + loadTemplatesByCategory, } }