diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json index 5daded9..a1feb0d 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json @@ -44,7 +44,7 @@ "code": "App.SiteManagement.Theme.Style", "nameKey": "App.SiteManagement.Theme.Style", "descriptionKey": "App.SiteManagement.Theme.Style.Description", - "defaultValue": "dx.material.blue.light.compact", + "defaultValue": "dx.material.blue.dark.compact", "isVisibleToClients": true, "providers": "U|G|D", "isInherited": false, diff --git a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json index ae31970..a47b0eb 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json +++ b/api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json @@ -10254,6 +10254,12 @@ "en": "Active", "tr": "Aktif" }, + { + "resourceName": "Platform", + "key": "App.Status.Inactive", + "en": "Inactive", + "tr": "Pasif" + }, { "resourceName": "Platform", "key": "App.DeveloperKit.ComponentEditor.Save", diff --git a/ui/src/components/codeLayout/ComponentCodeEditor.tsx b/ui/src/components/codeLayout/ComponentCodeEditor.tsx index c659223..bf3219c 100644 --- a/ui/src/components/codeLayout/ComponentCodeEditor.tsx +++ b/ui/src/components/codeLayout/ComponentCodeEditor.tsx @@ -13,9 +13,7 @@ interface ComponentCodeEditorProps { onResetCodeChanges: () => void language?: string theme?: 'vs-dark' | 'light' - parseError?: string | null onCursorChange?: (componentId: string | null) => void - onDrop?: (componentDef: ComponentDefinition, position: { line: number; column: number }) => void onComponentAdded?: (componentDef: ComponentDefinition) => void onComponentSave: () => void } @@ -24,6 +22,7 @@ export const ComponentCodeEditor: React.FC = ({ code, onChange, onApplyCodeChanges, + onResetCodeChanges, language = 'typescript', theme = 'vs-dark', onCursorChange, @@ -48,7 +47,6 @@ export const ComponentCodeEditor: React.FC = ({ const editorRef = useRef(null) const containerRef = useRef(null) - const cursorChangeTimeout = useRef(null) // Refs to keep latest values accessible inside native DOM listeners const localCodeRef = useRef(localCode) const dropCallbacksRef = useRef({ onChange, onComponentAdded, onApplyCodeChanges }) @@ -66,14 +64,6 @@ export const ComponentCodeEditor: React.FC = ({ dropCallbacksRef.current = { onChange, onComponentAdded, onApplyCodeChanges } }, [onChange, onComponentAdded, onApplyCodeChanges]) - useEffect(() => { - return () => { - if (cursorChangeTimeout.current) { - clearTimeout(cursorChangeTimeout.current) - } - } - }, []) - const findComponentIdAtPosition = ( code: string, position: { lineNumber: number; column: number }, @@ -169,7 +159,10 @@ export const ComponentCodeEditor: React.FC = ({ if (ch === '(') parenDepth++ else if (ch === ')') { parenDepth-- - if (parenDepth <= 0) { insideReturn = false; break } + if (parenDepth <= 0) { + insideReturn = false + break + } } } } @@ -178,7 +171,10 @@ export const ComponentCodeEditor: React.FC = ({ if (ch === '(') parenDepth++ else if (ch === ')') { parenDepth-- - if (parenDepth <= 0) { insideReturn = false; break } + if (parenDepth <= 0) { + insideReturn = false + break + } } } } @@ -186,15 +182,11 @@ export const ComponentCodeEditor: React.FC = ({ return insideReturn } - const handleEditorCursorChange = (_event: any) => { + const handleEditorCursorChange = () => { if (!editorRef.current) return const position = editorRef.current.getPosition() if (!position) return - // Throttle the cursor change to avoid too many calls - if (cursorChangeTimeout.current) { - clearTimeout(cursorChangeTimeout.current) - } const id = findComponentIdAtPosition(localCode, position) if (onCursorChange) onCursorChange(id) } @@ -242,12 +234,19 @@ export const ComponentCodeEditor: React.FC = ({ let componentDefData: string | undefined for (const fmt of ['application/json', 'text/plain']) { const d = nativeEvent.dataTransfer.getData(fmt) - if (d) { componentDefData = d; break } + if (d) { + componentDefData = d + break + } } if (!componentDefData) return let componentDef: any - try { componentDef = JSON.parse(componentDefData) } catch { return } + try { + componentDef = JSON.parse(componentDefData) + } catch { + return + } if (!componentDef?.name) return // Resolve the drop position inside Monaco @@ -300,8 +299,11 @@ export const ComponentCodeEditor: React.FC = ({ const newCode = newLines.join('\n') setLocalCode(newCode) - const { onChange: onChg, onComponentAdded: onAdded, onApplyCodeChanges: onApply } = - dropCallbacksRef.current + const { + onChange: onChg, + onComponentAdded: onAdded, + onApplyCodeChanges: onApply, + } = dropCallbacksRef.current onChg(newCode) if (onAdded) onAdded(componentDefWithId) setTimeout(() => onApply(newCode), 100) @@ -346,6 +348,7 @@ export const ComponentCodeEditor: React.FC = ({ const handleResetChanges = () => { setLocalCode(code) setHasChanges(false) + onResetCodeChanges() } // Drag & Drop handlers for Code Editor @@ -561,53 +564,53 @@ export const ComponentCodeEditor: React.FC = ({
@@ -622,8 +625,8 @@ export const ComponentCodeEditor: React.FC = ({ setWordWrap(e.target.value as 'on' | 'off')} className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" + onChange={(e) => setWordWrap(e.target.value as 'on' | 'off')} > @@ -659,11 +662,11 @@ export const ComponentCodeEditor: React.FC = ({
@@ -680,8 +683,6 @@ export const ComponentCodeEditor: React.FC = ({ language={language} theme={editorTheme} value={localCode} - onChange={handleCodeChange} - onMount={handleEditorDidMount} options={{ fontSize: fontSize, wordWrap: wordWrap, @@ -748,6 +749,8 @@ export const ComponentCodeEditor: React.FC = ({ formatOnPaste: true, formatOnType: true, }} + onChange={handleCodeChange} + onMount={handleEditorDidMount} />
diff --git a/ui/src/components/codeLayout/PropertyPanel.tsx b/ui/src/components/codeLayout/PropertyPanel.tsx index c870e5f..e19c4e6 100644 --- a/ui/src/components/codeLayout/PropertyPanel.tsx +++ b/ui/src/components/codeLayout/PropertyPanel.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import TailwindModal from "./TailwindModal"; import { ComponentInfo, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo"; import { getComponentDefinition } from "./data/componentDefinitions"; @@ -113,15 +113,6 @@ const PropertyPanel: React.FC = ({ setHasHookChanges(true); }; - // Handle local event changes - const handleLocalEventChange = (eventName: string, value: string) => { - setPendingEvents((prev) => ({ - ...prev, - [eventName]: value, - })); - setHasChanges(true); - }; - // Apply only property/event changes const handleApplyPropChanges = () => { if (!selectedComponent) return; @@ -222,7 +213,7 @@ const PropertyPanel: React.FC = ({ if (property.type === "array") { try { arrayInputValue = JSON.stringify(value, null, 2); - } catch (e) { + } catch { arrayInputValue = ""; arrayError = "Array verisi gösterilemiyor."; } @@ -365,7 +356,7 @@ const PropertyPanel: React.FC = ({ const parsed = JSON.parse(e.target.value); handleLocalPropertyChange(property.name, parsed); arrayError = ""; - } catch (err) { + } catch { arrayError = "Geçersiz JSON formatı"; } }} diff --git a/ui/src/components/codeLayout/TailwindModal.tsx b/ui/src/components/codeLayout/TailwindModal.tsx index 216ae6c..1d74d0a 100644 --- a/ui/src/components/codeLayout/TailwindModal.tsx +++ b/ui/src/components/codeLayout/TailwindModal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { searchTailwindClasses, TAILWIND_CLASSES } from './data/tailwindClasses'; import { Button } from '../ui'; import { FaTimes } from 'react-icons/fa'; @@ -40,12 +40,6 @@ const TailwindModal: React.FC = ({ // Don't close modal - let user select multiple classes }; - const handleAddToCurrentValue = (className: string) => { - const newValue = currentValue ? `${currentValue} ${className}` : className; - onSelectClass(newValue); - // Don't close modal - let user continue selecting - }; - if (!isOpen) return null; return ( diff --git a/ui/src/components/codeLayout/editorSettings/global.d.ts b/ui/src/components/codeLayout/editorSettings/global.d.ts index b45379d..530afd7 100644 --- a/ui/src/components/codeLayout/editorSettings/global.d.ts +++ b/ui/src/components/codeLayout/editorSettings/global.d.ts @@ -1,3 +1,5 @@ +import React from 'react'; + declare interface Window { ReactDOM?: { render: (element: React.ReactNode, container: Element) => void; diff --git a/ui/src/components/common/InfoSection.tsx b/ui/src/components/common/InfoSection.tsx index b01715d..04d726d 100644 --- a/ui/src/components/common/InfoSection.tsx +++ b/ui/src/components/common/InfoSection.tsx @@ -1,3 +1,4 @@ +import React from "react"; import classNames from "classnames"; interface InfoSectionProps { diff --git a/ui/src/components/common/Widget.tsx b/ui/src/components/common/Widget.tsx index 03e4a81..3132e7d 100644 --- a/ui/src/components/common/Widget.tsx +++ b/ui/src/components/common/Widget.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import classNames from "classnames"; import { iconList } from "./iconList"; diff --git a/ui/src/components/common/WidgetGroup.tsx b/ui/src/components/common/WidgetGroup.tsx index dadd6eb..eee6275 100644 --- a/ui/src/components/common/WidgetGroup.tsx +++ b/ui/src/components/common/WidgetGroup.tsx @@ -1,6 +1,5 @@ import classNames from "classnames"; import Widget, { type colorType } from "./Widget"; -import { useNavigate } from "react-router-dom"; import { WidgetEditDto, WidgetGroupDto } from "../../types/common"; export default function WidgetGroup({ @@ -8,8 +7,6 @@ export default function WidgetGroup({ }: { widgetGroups: WidgetGroupDto[]; }) { - const navigate = useNavigate(); - return (
{widgetGroups.map((group, gIdx) => ( diff --git a/ui/src/components/layouts/AuthLayout/Side.tsx b/ui/src/components/layouts/AuthLayout/Side.tsx index 4700aaa..8491e50 100644 --- a/ui/src/components/layouts/AuthLayout/Side.tsx +++ b/ui/src/components/layouts/AuthLayout/Side.tsx @@ -1,4 +1,4 @@ -import { cloneElement } from 'react' +import React, { cloneElement } from 'react' import Avatar from '@/components/ui/Avatar' import Logo from '@/components/template/Logo' import { APP_NAME } from '@/constants/app.constant' diff --git a/ui/src/components/layouts/DeveloperLayout.tsx b/ui/src/components/layouts/DeveloperLayout.tsx index e794866..3b22c8b 100644 --- a/ui/src/components/layouts/DeveloperLayout.tsx +++ b/ui/src/components/layouts/DeveloperLayout.tsx @@ -1,44 +1,10 @@ -import React from 'react' -import { FaTachometerAlt, FaDatabase, FaBolt, FaServer, FaPuzzlePiece } from 'react-icons/fa' -import { useLocalization } from '@/utils/hooks/useLocalization' -import { useLocation, useNavigate } from 'react-router-dom' -import { ROUTES_ENUM } from '@/routes/route.constant' +import type { PropsWithChildren } from 'react' import { Container } from '../shared' -interface DeveloperLayoutProps { - children: React.ReactNode -} - -const DeveloperLayout: React.FC = ({ children }) => { - const { translate } = useLocalization() - const location = useLocation() - const navigate = useNavigate() - - const navigation = [ - { - id: 'entities', - label: translate('::App.DeveloperKit.Entity'), - icon: FaDatabase, - path: ROUTES_ENUM.protected.saas.developerKit.entities, - }, - { - id: 'endpoints', - label: translate('::App.DeveloperKit.CrudEndpoints'), - icon: FaServer, - path: ROUTES_ENUM.protected.saas.developerKit.endpoints, - }, - { - id: 'components', - label: translate('::App.DeveloperKit.Components'), - icon: FaPuzzlePiece, - path: ROUTES_ENUM.protected.saas.developerKit.components, - }, - ] - +const DeveloperLayout = ({ children }: PropsWithChildren) => { return (
- {/* Main Content */}
{children}
diff --git a/ui/src/components/orders/BillingControls.tsx b/ui/src/components/orders/BillingControls.tsx index 7fbbe95..da1e0d2 100644 --- a/ui/src/components/orders/BillingControls.tsx +++ b/ui/src/components/orders/BillingControls.tsx @@ -23,7 +23,6 @@ export const BillingControls: React.FC = ({ setGlobalBillingCycle, setGlobalPeriod, onCartClick, - onNavigate, cartItemsCount, cartState, }) => { diff --git a/ui/src/components/orders/OrderSuccess.tsx b/ui/src/components/orders/OrderSuccess.tsx index 4c53cb5..902c155 100644 --- a/ui/src/components/orders/OrderSuccess.tsx +++ b/ui/src/components/orders/OrderSuccess.tsx @@ -10,7 +10,7 @@ interface OrderSuccessProps { onBackToShop: () => void } -export const OrderSuccess: React.FC = ({ orderId, onBackToShop }) => { +export const OrderSuccess: React.FC = ({ orderId }) => { const navigate = useNavigate() const { translate } = useLocalization() diff --git a/ui/src/components/orders/ProductCard.tsx b/ui/src/components/orders/ProductCard.tsx index 1e21b8b..d9a8c9d 100644 --- a/ui/src/components/orders/ProductCard.tsx +++ b/ui/src/components/orders/ProductCard.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { FaPlus, FaMinus } from 'react-icons/fa' -import { BillingCycle, Product, ProductDto } from '@/proxy/order/models' +import { BillingCycle, ProductDto } from '@/proxy/order/models' import { CartState } from '@/utils/cartUtils' import { useLocalization } from '@/utils/hooks/useLocalization' import { Button } from '@/components/ui' diff --git a/ui/src/components/shared/ActionLink.tsx b/ui/src/components/shared/ActionLink.tsx index 9acbeaf..7ddd2cc 100644 --- a/ui/src/components/shared/ActionLink.tsx +++ b/ui/src/components/shared/ActionLink.tsx @@ -4,14 +4,14 @@ import { Link } from 'react-router-dom' import type { CommonProps } from '@/proxy/common' import type { ComponentPropsWithoutRef } from 'react' -interface ActionLink extends CommonProps, ComponentPropsWithoutRef<'a'> { +interface ActionLinkProps extends CommonProps, ComponentPropsWithoutRef<'a'> { themeColor?: boolean to?: string href?: string reloadDocument?: boolean } -const ActionLink = (props: ActionLink) => { +const ActionLink = (props: ActionLinkProps) => { const { children, className, diff --git a/ui/src/components/shared/DbMigrateLogPanel.tsx b/ui/src/components/shared/DbMigrateLogPanel.tsx index 25e62aa..8420cb3 100644 --- a/ui/src/components/shared/DbMigrateLogPanel.tsx +++ b/ui/src/components/shared/DbMigrateLogPanel.tsx @@ -1,5 +1,4 @@ import { MigrateLogEntry } from '@/proxy/setup/models' -import { useLocalization } from '@/utils/hooks/useLocalization' import { useEffect, useRef, useState } from 'react' import { createRoot } from 'react-dom/client' import { Button } from '@/components/ui' diff --git a/ui/src/components/shared/SegmentItemOption.tsx b/ui/src/components/shared/SegmentItemOption.tsx index 2b67b59..febddf2 100644 --- a/ui/src/components/shared/SegmentItemOption.tsx +++ b/ui/src/components/shared/SegmentItemOption.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from 'react' +import React, { forwardRef } from 'react' import classNames from 'classnames' import useThemeClass from '@/utils/hooks/useThemeClass' import { FaCheckCircle } from 'react-icons/fa' diff --git a/ui/src/contexts/ComponentContext.tsx b/ui/src/contexts/ComponentContext.tsx index 1821872..c0dd121 100644 --- a/ui/src/contexts/ComponentContext.tsx +++ b/ui/src/contexts/ComponentContext.tsx @@ -179,34 +179,58 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi useEffect(() => { // Example components for testing const HelloWorldComponent: React.ComponentType = () => { - return React.createElement('div', { - className: 'p-6 bg-blue-50 rounded-lg' - }, [ - React.createElement('h1', { - className: 'text-2xl font-bold text-blue-900 mb-4' - }, 'Hello World!'), - React.createElement('p', { - className: 'text-blue-700' - }, 'Bu manuel kayıtlı bir komponent!') - ]) + return React.createElement( + 'div', + { + className: 'p-6 bg-blue-50 rounded-lg', + }, + [ + React.createElement( + 'h1', + { + className: 'text-2xl font-bold text-blue-900 mb-4', + }, + 'Hello World!', + ), + React.createElement( + 'p', + { + className: 'text-blue-700', + }, + 'Bu manuel kayıtlı bir komponent!', + ), + ], + ) } const TestListComponent: React.ComponentType = () => { - return React.createElement('div', { - className: 'p-6 bg-green-50 rounded-lg' - }, [ - React.createElement('h1', { - className: 'text-2xl font-bold text-green-900 mb-4' - }, 'Test List'), - React.createElement('p', { - className: 'text-green-700' - }, 'Bu da test için kayıtlı komponent!') - ]) + return React.createElement( + 'div', + { + className: 'p-6 bg-green-50 rounded-lg', + }, + [ + React.createElement( + 'h1', + { + className: 'text-2xl font-bold text-green-900 mb-4', + }, + 'Test List', + ), + React.createElement( + 'p', + { + className: 'text-green-700', + }, + 'Bu da test için kayıtlı komponent!', + ), + ], + ) } registerComponent('HelloWorld', HelloWorldComponent) registerComponent('TestList', TestListComponent) - }, []) // Empty dependency array - only run once + }, []) // Empty dependency array - only run once // Component compilation functions (moved from ComponentRegistryProvider) const extractComponentInfo = useCallback((code: string, defaultName = '') => { @@ -269,8 +293,8 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi const componentDefinitions = componentInfos .map((info) => { const componentVariables = componentInfos - .filter(other => other.name !== info.name) - .map(other => `const ${other.name} = ${other.name}_Component;`) + .filter((other) => other.name !== info.name) + .map((other) => `const ${other.name} = ${other.name}_Component;`) .join('\n ') return ` @@ -314,12 +338,90 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi } const componentsFactory = new Function( - 'React', 'Alert', 'Avatar', 'Badge', 'Button', 'Calendar', 'Card', 'Checkbox', 'ConfigProvider', 'DatePicker', 'Dialog', 'Drawer', 'Dropdown', 'FormItem', 'FormContainer', 'Input', 'InputGroup', 'Menu', 'MenuItem', 'Notification', 'Pagination', 'Progress', 'Radio', 'RangeCalendar', 'ScrollBar', 'Segment', 'Select', 'Skeleton', 'Spinner', 'Steps', 'Switcher', 'Table', 'Tabs', 'Tag', 'TimeInput', 'Timeline', 'toast', 'Tooltip', 'Upload', 'axios', + 'React', + 'Alert', + 'Avatar', + 'Badge', + 'Button', + 'Calendar', + 'Card', + 'Checkbox', + 'ConfigProvider', + 'DatePicker', + 'Dialog', + 'Drawer', + 'Dropdown', + 'FormItem', + 'FormContainer', + 'Input', + 'InputGroup', + 'Menu', + 'MenuItem', + 'Notification', + 'Pagination', + 'Progress', + 'Radio', + 'RangeCalendar', + 'ScrollBar', + 'Segment', + 'Select', + 'Skeleton', + 'Spinner', + 'Steps', + 'Switcher', + 'Table', + 'Tabs', + 'Tag', + 'TimeInput', + 'Timeline', + 'toast', + 'Tooltip', + 'Upload', + 'axios', `return ${compiledBundle}`, ) const compiledComponentsRegistry = componentsFactory( - React, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, ConfigProvider, DatePicker, Dialog, Drawer, Dropdown, FormItem, FormContainer, Input, InputGroup, Menu, MenuItem, Notification, Pagination, Progress, Radio, RangeCalendar, ScrollBar, Segment, Select, Skeleton, Spinner, Steps, Switcher, Table, Tabs, Tag, TimeInput, Timeline, toast, Tooltip, Upload, axios, + React, + Alert, + Avatar, + Badge, + Button, + Calendar, + Card, + Checkbox, + ConfigProvider, + DatePicker, + Dialog, + Drawer, + Dropdown, + FormItem, + FormContainer, + Input, + InputGroup, + Menu, + MenuItem, + Notification, + Pagination, + Progress, + Radio, + RangeCalendar, + ScrollBar, + Segment, + Select, + Skeleton, + Spinner, + Steps, + Switcher, + Table, + Tabs, + Tag, + TimeInput, + Timeline, + toast, + Tooltip, + Upload, + axios, ) setCompiledComponents(compiledComponentsRegistry) @@ -352,14 +454,11 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi [components, compiledComponents], ) - const compileAndRender = useCallback( - (code: string, props: ComponentProps = {}) => { - if (!code?.trim()) return null - // Simplified version - can be extended later - return
Code compilation not implemented yet
- }, - [], - ) + const compileAndRender = useCallback((code: string) => { + if (!code?.trim()) return null + // Simplified version - can be extended later + return
Code compilation not implemented yet
+ }, []) const isComponentRegistered = useCallback( (name: string) => { diff --git a/ui/src/contexts/DemoContext.tsx b/ui/src/contexts/DemoContext.tsx index 369cd0e..a62dfbf 100644 --- a/ui/src/contexts/DemoContext.tsx +++ b/ui/src/contexts/DemoContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext } from 'react' +import { createContext, useContext } from 'react' interface DemoContextValue { openDemo: () => void diff --git a/ui/src/contexts/ScrollContext.tsx b/ui/src/contexts/ScrollContext.tsx index 6d3ddba..ec65d32 100644 --- a/ui/src/contexts/ScrollContext.tsx +++ b/ui/src/contexts/ScrollContext.tsx @@ -1,5 +1,5 @@ // ScrollContext.tsx -import React, { createContext, useContext } from 'react' +import { createContext, useContext } from 'react' export const ScrollContext = createContext(false) diff --git a/ui/src/routes/dynamicRouteLoader.tsx b/ui/src/routes/dynamicRouteLoader.tsx index 2e8beb9..654bdc3 100644 --- a/ui/src/routes/dynamicRouteLoader.tsx +++ b/ui/src/routes/dynamicRouteLoader.tsx @@ -1,5 +1,5 @@ +import React, { lazy } from 'react' import { RouteDto } from '@/proxy/routes/models' -import { lazy } from 'react' // Tüm view bileşenlerini import et (vite özel) // shared klasörü hariç, çünkü bu bileşenler genellikle başka yerlerde statik import ediliyor diff --git a/ui/src/services/admin/list-form.service.ts b/ui/src/services/admin/list-form.service.ts index c076dfb..1bff277 100644 --- a/ui/src/services/admin/list-form.service.ts +++ b/ui/src/services/admin/list-form.service.ts @@ -50,7 +50,7 @@ export const deleteListFormJsonRow = (id: string, field: string, index: number) url: `/api/admin/list-form-json-row/${id}?field=${field}&index=${index}`, }) -export const getListFormSelectQueryByCode = (listFormCode?: string, op: number = -1) => +export const getListFormSelectQueryByCode = (listFormCode?: string) => apiService.fetchData({ method: 'GET', url: `/api/admin/list-form-query-preview/form-select-query`, diff --git a/ui/src/services/note.service.ts b/ui/src/services/note.service.ts index a0e9576..293b906 100644 --- a/ui/src/services/note.service.ts +++ b/ui/src/services/note.service.ts @@ -56,7 +56,7 @@ class NoteService { }) } - async downloadFile(savedFileName: string, fileName: string, fileType: string) { + async downloadFile(savedFileName: string, fileName: string) { const response = await apiService.fetchData({ url: `/api/app/note/download?savedFileName=${savedFileName}`, method: 'GET', diff --git a/ui/src/services/order.service.ts b/ui/src/services/order.service.ts index 1735d1c..164e669 100644 --- a/ui/src/services/order.service.ts +++ b/ui/src/services/order.service.ts @@ -1,5 +1,5 @@ import { InstallmentOptionDto, OrderDto, PaymentMethodDto, ProductDto } from '@/proxy/order/models' -import apiService, { Config } from '@/services/api.service' +import apiService from '@/services/api.service' import axios from 'axios' export class OrderService { diff --git a/ui/src/services/platformApi.service.ts b/ui/src/services/platformApi.service.ts index 6107868..1a1673b 100644 --- a/ui/src/services/platformApi.service.ts +++ b/ui/src/services/platformApi.service.ts @@ -111,7 +111,7 @@ platformApiService.interceptors.response.use( signOut() error.silent = true } - } catch (error2) { + } catch { await revokeTokens({ token: auth.session.token, refreshToken: auth.session.refreshToken, diff --git a/ui/src/types/hr.ts b/ui/src/types/hr.ts deleted file mode 100644 index 0e778f0..0000000 --- a/ui/src/types/hr.ts +++ /dev/null @@ -1,663 +0,0 @@ -import { DepartmentDto, EmployeeDto } from '@/proxy/intranet/models' - -export interface Address { - // Adres - country: string - state: string - city: string - street: string - postalCode: string -} - -export interface HrCostCenter { - // İnsan Kaynakları Masraf Merkezi - id: string - code: string - name: string - description?: string - parentCostCenterId?: string - parentCostCenter?: HrCostCenter - subCostCenters: HrCostCenter[] - responsibleEmployeeId?: string - responsibleEmployee?: EmployeeDto - departmentId?: string - department?: DepartmentDto - costCenterType: CostCenterType - budgetedAmount: number - actualAmount: number - currency: string - fiscalYear: string - isActive: boolean - creationTime: Date - lastModificationTime: Date -} - -export interface HrPerformanceEvaluation { - // İnsan Kaynakları Performans Değerlendirmesi - id: string - employeeId: string - employee?: EmployeeDto - evaluatorId: string - evaluator?: EmployeeDto - evaluationPeriod: string - evaluationType: EvaluationTypeEnum - overallRating: number - goals: HrPerformanceGoal[] - competencies: HrCompetencyRating[] - strengths: string[] - areasForImprovement: string[] - developmentPlan: string[] - comments?: string - status: EvaluationStatusEnum - dueDate: Date - completedDate?: Date - creationTime: Date - lastModificationTime: Date -} - -export interface HrPerformanceGoal { - // İnsan Kaynakları Performans Hedefi - id: string - evaluationId: string - goalDescription: string - targetValue?: string - actualValue?: string - weight: number - rating: number - comments?: string -} - -export interface HrCompetencyRating { - // İnsan Kaynakları Yeterlilik Değerlendirmesi - id: string - evaluationId: string - competencyName: string - expectedLevel: number - actualLevel: number - rating: number - comments?: string -} - -export interface HrTraining { - // İnsan Kaynakları Eğitimi - id: string - trainingCode: string - title: string - description?: string - trainingType: TrainingTypeEnum - provider: string - startDate: Date - endDate: Date - duration: number - location?: string - isOnline: boolean - maxParticipants?: number - cost: number - currency: string - status: TrainingStatusEnum - participants: HrTrainingParticipant[] - materials: string[] - creationTime: Date - lastModificationTime: Date -} - -export interface HrTrainingParticipant { - // İnsan Kaynakları Eğitim Katılımcısı - id: string - trainingId: string - employeeId: string - employee?: EmployeeDto - enrollmentDate: Date - completionDate?: Date - status: ParticipationStatusEnum - score?: number - feedback?: string - certificateIssued: boolean -} - -export interface HrWorkSchedule { - // İnsan Kaynakları Çalışma Programı - id: string - scheduleCode: string - name: string - description?: string - workingDays: HrWorkingDay[] - totalHoursPerWeek: number - isFlexible: boolean - isActive: boolean -} - -export interface HrWorkingDay { - // İnsan Kaynakları Çalışma Günü - dayOfWeek: number // 0-6 (Sunday-Saturday) - isWorkingDay: boolean - startTime?: string - endTime?: string - breakDuration?: number // minutes -} - -export interface HrDisciplinaryAction { - // İnsan Kaynakları Disiplin Cezası - id: string - employeeId: string - employee?: EmployeeDto - actionType: DisciplinaryActionTypeEnum - reason: string - description: string - actionDate: Date - issuedBy: string - severity: DisciplinarySeverityEnum - status: DisciplinaryStatusEnum - expiryDate?: Date - attachments: string[] - creationTime: Date - lastModificationTime: Date -} - -export interface HrEmergencyContact { - // İnsan Kaynakları Acil Durum İletişim - name: string - relationship: string - phoneNumber: string - email?: string - address?: Address -} - -export interface HrEmploymentType { - // İnsan Kaynakları İstihdam Türü - id: string - name: string - count?: number - creationTime: Date - lastModificationTime: Date -} - -export interface HrPayroll { - // İnsan Kaynakları Maaş Bordrosu - id: string - employeeId: string - employee?: EmployeeDto - period: string - baseSalary: number - allowances: HrPayrollAllowance[] // İnsan Kaynakları Maaş Ek Ödemeleri - deductions: HrPayrollDeduction[] // İnsan Kaynakları Maaş Kesintileri - overtime: number - bonus: number - grossSalary: number - netSalary: number - tax: number - socialSecurity: number - status: PayrollStatusEnum - paymentDate?: Date - creationTime: Date - lastModificationTime: Date -} - -export interface HrPayrollAllowance { - // İnsan Kaynakları Maaş Ek Ödemesi - id: string - name: string - amount: number - taxable: boolean -} - -export interface HrPayrollDeduction { - // İnsan Kaynakları Maaş Kesintisi - id: string - name: string - amount: number - mandatory: boolean -} - -export interface HrBadge { - // İnsan Kaynakları Rozeti - id: string - code: string - name: string - description: string - icon: string - color: string - backgroundColor: string - category: BadgeCategoryEnum - criteria: string - points: number - rarity: BadgeRarityEnum - isActive: boolean - creationTime: Date - lastModificationTime: Date -} - -export interface HrOrganizationChart { - // İnsan Kaynakları Organizasyon Şeması - id: string - employeeId: string - employee?: EmployeeDto - parentId?: string - parent?: HrOrganizationChart - children?: HrOrganizationChart[] - level: number - position: string - isActive: boolean -} - -export interface HrEmployeeBadge { - // İnsan Kaynakları Çalışan Rozeti - id: string - employeeId: string - employee?: EmployeeDto - badgeId: string - badge?: HrBadge - earnedDate: Date - expiryDate?: Date - reason?: string - notes?: string - isActive: boolean - creationTime: Date - lastModificationTime: Date -} - -export interface HrEvaluation360Template { - // İnsan Kaynakları 360 Derece Değerlendirme Şablonu - id: string - name: string - description: string - isActive: boolean - questionGroups: HrQuestionGroup[] - applicablePositions: string[] // JobPosition ID'leri - applicableDepartments: string[] // Department ID'leri - assessorTypes: AssessorTypeEnum[] // Değerlendiriciler - creationTime: Date - lastModificationTime: Date -} - -export interface HrQuestionGroup { - // İnsan Kaynakları Soru Grubu - id: string - templateId: string - groupName: string - description?: string - weight: number // Bu grubun toplam puandaki ağırlığı (%) - order: number - questions: HrEvaluationQuestion[] -} - -export interface HrEvaluationQuestion { - // İnsan Kaynakları Değerlendirme Sorusu - id: string - groupId: string - questionText: string - questionType: QuestionTypeEnum - isRequired: boolean - weight: number // Bu sorunun grup içindeki ağırlığı (%) - order: number - options?: HrQuestionOption[] // Çoktan seçmeli sorular için - minRating?: number - maxRating?: number - ratingLabels?: string[] // Rating için etiketler (örn: ["Zayıf", "Orta", "İyi", "Mükemmel"]) -} - -export interface HrQuestionOption { - // İnsan Kaynakları Soru Seçeneği - id: string - questionId: string - optionText: string - value: number - order: number -} - -export interface HrEvaluation360 { - // İnsan Kaynakları 360 Derece Değerlendirme Kampanyası - id: string - name: string - description: string - templateId: string - template?: HrEvaluation360Template - evaluationPeriod: string - startDate: Date - endDate: Date - status: CampaignStatusEnum - departmentId?: string // Hedef departman - targetEmployees: string[] // Değerlendirilecek Employee ID'leri - settings: HrEvaluation360Settings - creationTime: Date - lastModificationTime: Date -} - -export interface HrEvaluation360Settings { - // İnsan Kaynakları 360 Derece Değerlendirme Ayarları - allowSelfEvaluation: boolean - requireManagerEvaluation: boolean - minPeerEvaluations: number - minSubordinateEvaluations: number - allowAnonymousFeedback: boolean - sendReminderEmails: boolean - reminderIntervalDays: number -} - -export interface HrEvaluation360Participant { - // İnsan Kaynakları 360 Derece Değerlendirme Katılımcısı - id: string - campaignId: string - evaluatedEmployeeId: string // Değerlendirilen kişi - evaluatedEmployee?: EmployeeDto - evaluatorId: string // Değerlendiren kişi - evaluator?: EmployeeDto - evaluatorType: AssessorTypeEnum - status: ParticipantStatusEnum - invitedDate: Date - startedDate?: Date - completedDate?: Date - responses: HrEvaluation360Response[] - overallScore?: number - notes?: string -} - -export interface HrEvaluation360Response { - // İnsan Kaynakları 360 Derece Değerlendirme Cevabı - id: string - participantId: string - questionId: string - question?: HrEvaluationQuestion - responseValue: string | number // Cevap değeri - responseText?: string // Metin cevaplar için - score: number // Hesaplanan puan - submittedDate: Date -} - -export interface HrEvaluation360Result { - // İnsan Kaynakları 360 Derece Değerlendirme Sonucu - id: string - campaignId: string - employeeId: string - employee?: EmployeeDto - participants: HrEvaluation360Participant[] // Bu sonuca katkıda bulunan değerlendiriciler - overallScore: number - maxPossibleScore: number - scorePercentage: number - groupScores: HrGroupScore[] - assessorTypeScores: HrAssessorTypeScore[] - strengths: string[] - developmentAreas: string[] - actionPlan: string[] - managerComments?: string - hrComments?: string - status: ResultStatusEnum - generatedDate: Date - approvedBy?: string - approvedDate?: Date -} - -export interface HrGroupScore { - // İnsan Kaynakları Grup Puanı - groupId: string - groupName: string - score: number - maxScore: number - percentage: number - responseCount: number -} - -export interface HrAssessorTypeScore { - // İnsan Kaynakları Değerlendirici Türü Puanı - assessorType: AssessorTypeEnum - assessorCount: number - averageScore: number - maxScore: number - percentage: number -} - -export enum PayrollStatusEnum { - // Maaş Durumu - Draft = 'DRAFT', // Taslak - Calculated = 'CALCULATED', // Hesaplandı - Approved = 'APPROVED', // Onaylandı - Paid = 'PAID', // Ödendi - Cancelled = 'CANCELLED', // İptal edildi -} - -export enum GoalStatusEnum { - // Hedef Durumu - NotStarted = 'NOT_STARTED', // Başlanmadı - InProgress = 'IN_PROGRESS', // Devam Ediyor - Completed = 'COMPLETED', // Tamamlandı - Overdue = 'OVERDUE', // Geçmiş - Cancelled = 'CANCELLED', // İptal edildi -} - -export enum BadgeCategoryEnum { - // Rozet Kategorisi - Performance = 'PERFORMANCE', // Performans - Leadership = 'LEADERSHIP', // Liderlik - Innovation = 'INNOVATION', // Yenilik - Teamwork = 'TEAMWORK', // Takım Çalışması - Customer = 'CUSTOMER', // Müşteri - Safety = 'SAFETY', // Güvenlik - Attendance = 'ATTENDANCE', // Devamsızlık - Training = 'TRAINING', // Eğitim - Project = 'PROJECT', // Proje - Special = 'SPECIAL', // Özel -} - -export enum BadgeRarityEnum { - // Rozet Nadirliği - Common = 'COMMON', // Orta - Uncommon = 'UNCOMMON', // Yaygın olmayan - Rare = 'RARE', // Nadir - Epic = 'EPIC', // Epik - Legendary = 'LEGENDARY', // Efsanevi -} - -export enum ResultStatusEnum { - // Sonuç Durumu - Pending = 'PENDING', // Beklemede - InReview = 'IN_REVIEW', // İnceleniyor - Approved = 'APPROVED', // Onaylandı - Published = 'PUBLISHED', // Yayınlandı -} - -export enum GenderEnum { - // Cinsiyet - Male = 'MALE', // Erkek - Female = 'FEMALE', // Kadın - Other = 'OTHER', // Diğer -} - -export enum MaritalStatusEnum { - // Medeni Durum - Single = 'SINGLE', // Bekar - Married = 'MARRIED', // Evli - Divorced = 'DIVORCED', // Boşanmış - Widowed = 'WIDOWED', // Dul -} - -export enum CostCenterType { - // Masraf Merkezi Türü - Revenue = 'REVENUE', // Gelir - Standard = 'STANDARD', // Standart - Discretionary = 'DISCRETIONARY', // İsteğe bağlı - Investment = 'INVESTMENT', // Yatırım - Service = 'SERVICE', // Hizmet - Production = 'PRODUCTION', // Üretim - Support = 'SUPPORT', // Destek - Administrative = 'ADMINISTRATIVE', // İdari -} - -export enum EmploymentTypeEnum { - // İstihdam Türü - FullTime = 'FULL_TIME', // Tam Zamanlı - PartTime = 'PART_TIME', // Yarı Zamanlı - Contract = 'CONTRACT', // Sözleşmeli - Intern = 'INTERN', // Stajyer - Temporary = 'TEMPORARY', // Geçici - Consultant = 'CONSULTANT', // Danışman -} - -export enum EmployeeStatusEnum { - // Çalışan Durumu - Active = 'ACTIVE', // Aktif - Inactive = 'INACTIVE', // Pasif - OnLeave = 'ON_LEAVE', // İzinli - Suspended = 'SUSPENDED', // Askıya Alındı - Terminated = 'TERMINATED', // İşten Ayrıldı -} - -export enum JobLevelEnum { - // İş Seviyesi - Entry = 'ENTRY', // Giriş - Junior = 'JUNIOR', // Junior - Mid = 'MID', // Orta - Senior = 'SENIOR', // Kıdemli - Lead = 'LEAD', // Lider - Manager = 'MANAGER', // Yönetici - Director = 'DIRECTOR', // Direktör - Executive = 'EXECUTIVE', // İcra -} - -export enum LeaveTypeEnum { - // İzin Türü - Annual = 'Annual', // Yıllık - Sick = 'Sick', // Hastalık - Maternity = 'Maternity', // Doğum - Paternity = 'Paternity', // Babalık - Personal = 'Personal', // Kişisel - Emergency = 'Emergency', // Acil - Study = 'Study', // Eğitim - Unpaid = 'Unpaid', // Ücretsiz -} - -export enum LeaveStatusEnum { - // İzin Durumu - Pending = 'Pending', // Beklemede - Approved = 'Approved', // Onaylandı - Rejected = 'Rejected', // Reddedildi - Cancelled = 'Cancelled', // İptal edildi -} - -export enum EvaluationStatusEnum { - // Değerlendirme Durumu - NotStarted = 'NOT_STARTED', // Başlanmadı - InProgress = 'IN_PROGRESS', // Devam Ediyor - PendingReview = 'PENDING_REVIEW', // İnceleme Bekliyor - Completed = 'COMPLETED', // Tamamlandı - Overdue = 'OVERDUE', // Geçmiş -} - -export enum TrainingTypeEnum { - // Eğitim Türü - Technical = 'TECHNICAL', // Teknik - Soft_Skills = 'SOFT_SKILLS', // Yumuşak Beceriler - Leadership = 'LEADERSHIP', // Liderlik - Compliance = 'COMPLIANCE', // Uyum - Safety = 'SAFETY', // Güvenlik - Product = 'PRODUCT', // Ürün - Process = 'PROCESS', // Süreç -} - -export enum TrainingStatusEnum { - // Eğitim Durumu - Planned = 'PLANNED', // Planlandı - Open = 'OPEN', // Açık - InProgress = 'IN_PROGRESS', // Devam Ediyor - Completed = 'COMPLETED', // Tamamlandı - Cancelled = 'CANCELLED', // İptal Edildi -} - -export enum ParticipationStatusEnum { - // Katılım Durumu - Enrolled = 'ENROLLED', // Kayıtlı - InProgress = 'IN_PROGRESS', // Devam Ediyor - Completed = 'COMPLETED', // Tamamlandı - Failed = 'FAILED', // Başarısız - Withdrawn = 'WITHDRAWN', // Çekildi -} - -export enum DisciplinaryActionTypeEnum { - // Disiplin Cezası Türü - Verbal_Warning = 'VERBAL_WARNING', // Sözlü Uyarı - Written_Warning = 'WRITTEN_WARNING', // Yazılı Uyarı - Final_Warning = 'FINAL_WARNING', // Son Uyarı - Suspension = 'SUSPENSION', // Askıya Alma - Termination = 'TERMINATION', // İşten Çıkarma - Demotion = 'DEMOTION', // Aşağı Alma -} - -export enum DisciplinarySeverityEnum { - // Disiplin Cezası Şiddeti - Minor = 'MINOR', // Hafif - Major = 'MAJOR', // Ağır - Severe = 'SEVERE', // Ciddi -} - -export enum DisciplinaryStatusEnum { - // Disiplin Cezası Durumu - Active = 'ACTIVE', // Aktif - Resolved = 'RESOLVED', // Çözüldü - Expired = 'EXPIRED', // Süresi Dolmuş - Appealed = 'APPEALED', // İtiraz Edilmiş -} - -export enum AssessorTypeEnum { - // Değerlendirici Türü - Self = 'SELF', // Kendi - Manager = 'MANAGER', // Yönetici - Peer = 'PEER', // Meslektaş - Subordinate = 'SUBORDINATE', // Ast - Customer = 'CUSTOMER', // Müşteri - OtherDepartment = 'OTHER_DEPARTMENT', // Diğer Departman - HRUpperManagement = 'HR_UPPER_MANAGEMENT', // İK/Üst Yönetim - External = 'EXTERNAL', // Dış paydaş değerlendirmesi (mevcut) -} - -export enum EvaluationTypeEnum { - // Değerlendirme Türü - Annual = 'ANNUAL', // Yıllık - Quarterly = 'QUARTERLY', // Çeyrek - Probation = 'PROBATION', // Deneme Süresi - Project = 'PROJECT', // Proje - Competency = 'COMPETENCY', // Yeterlilik - Leadership = 'LEADERSHIP', // Liderlik - Degree360 = 'DEGREE_360', // 360° Derece değerlendirme -} - -export enum CampaignStatusEnum { - // Kampanya Durumu - Draft = 'DRAFT', // Taslak - Active = 'ACTIVE', // Aktif - Completed = 'COMPLETED', // Tamamlandı - Cancelled = 'CANCELLED', // İptal Edildi -} - -export enum ParticipantStatusEnum { - // Katılımcı Durumu - Invited = 'INVITED', // Davet Edildi - Started = 'STARTED', // Başladı - Completed = 'COMPLETED', // Tamamlandı - Expired = 'EXPIRED', // Süresi Dolmuş - Declined = 'DECLINED', // Reddedildi -} - -export enum QuestionTypeEnum { - // Soru Türü - Rating = 'RATING', // 1-5 puan verme - MultipleChoice = 'MULTIPLE_CHOICE', // Çoktan seçmeli - Text = 'TEXT', // Metin cevap - YesNo = 'YES_NO', // Evet/Hayır - Scale = 'SCALE', // Ölçek (1-10 gibi) -} - -export const getQuestionTypeText = (type: QuestionTypeEnum) => { - switch (type) { - case QuestionTypeEnum.Rating: - return 'Puanlama' - case QuestionTypeEnum.MultipleChoice: - return 'Çoktan Seçmeli' - case QuestionTypeEnum.Text: - return 'Metin' - case QuestionTypeEnum.YesNo: - return 'Evet/Hayır' - case QuestionTypeEnum.Scale: - return 'Ölçek' - default: - return 'Bilinmeyen' - } -} diff --git a/ui/src/utils/deepParseJson.ts b/ui/src/utils/deepParseJson.ts index 362386c..14ea4d8 100644 --- a/ui/src/utils/deepParseJson.ts +++ b/ui/src/utils/deepParseJson.ts @@ -11,7 +11,7 @@ function deepParseJson(jsonString: Json): Json { } try { return deepParseJson(JSON.parse(jsonString)) - } catch (err) { + } catch { return jsonString } } else if (Array.isArray(jsonString)) { diff --git a/ui/src/views/admin/activityLog/ActivityLog.tsx b/ui/src/views/admin/activityLog/ActivityLog.tsx index f8437dd..0b363d6 100644 --- a/ui/src/views/admin/activityLog/ActivityLog.tsx +++ b/ui/src/views/admin/activityLog/ActivityLog.tsx @@ -7,12 +7,8 @@ import { NotificationDto } from '@/proxy/notification/models' import { getList } from '@/services/notification.service' import { useLocalization } from '@/utils/hooks/useLocalization' import dayjs from 'dayjs' -import { Dictionary } from 'lodash' -import forOwn from 'lodash/forOwn' import groupBy from 'lodash/groupBy' -import has from 'lodash/has' -import merge from 'lodash/merge' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import Log from './components/Log' import LogFilter from './components/LogFilter' import { Helmet } from 'react-helmet' @@ -21,12 +17,13 @@ import { DIR_RTL } from '@/constants/theme.constant' import { useStoreState } from '@/store' const itemsPerPage = 10 +type NotificationGroups = Record const ActivityLog = () => { const { translate } = useLocalization() const [loading, setLoading] = useState(false) - const [notifications, setNotifications] = useState>({}) + const [notifications, setNotifications] = useState({}) const [page, setPage] = useState(0) const [hasMore, setHasMore] = useState(false) const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false) @@ -41,38 +38,43 @@ const ActivityLog = () => { NotificationChannels.WhatsApp, ]) - const fetchData = async (isLoadmore: boolean = false) => { + const fetchData = useCallback(async (isLoadmore: boolean = false) => { setLoading(true) - const response = await getList({ - maxResultCount: itemsPerPage, - skipCount: page * itemsPerPage, - sorting: 'CreationTime DESC', - channels: filter, - isListRequest: true, - }) - const items = response.data?.items ?? [] - - const currentItemCount = page * itemsPerPage + items.length - setHasMore((response.data?.totalCount ?? 0) > currentItemCount) - const logs = groupBy(items, (a) => dayjs(a.creationTime).format('YYYY-MM-DD')) - - if (isLoadmore) { - forOwn(notifications, function (value, key) { - if (has(logs, key)) { - notifications[key] = [...notifications[key], ...logs[key]] - delete logs[key] - } + try { + const response = await getList({ + maxResultCount: itemsPerPage, + skipCount: page * itemsPerPage, + sorting: 'CreationTime DESC', + channels: filter, + isListRequest: true, }) - setNotifications(merge(notifications, logs)) - } else { - setNotifications(logs) + const items = response.data?.items ?? [] + + const currentItemCount = page * itemsPerPage + items.length + setHasMore((response.data?.totalCount ?? 0) > currentItemCount) + const logs = groupBy(items, (a) => dayjs(a.creationTime).format('YYYY-MM-DD')) + + if (isLoadmore) { + setNotifications((prev) => { + const next: NotificationGroups = { ...prev } + + for (const [key, value] of Object.entries(logs)) { + next[key] = [...(next[key] ?? []), ...value] + } + + return next + }) + } else { + setNotifications(logs) + } + } finally { + setLoading(false) } - setLoading(false) - } + }, [filter, page]) useEffect(() => { fetchData(page > 0) - }, [page, filter]) + }, [fetchData, page]) const handleFilterChange = (value: string[]) => { setPage(0) @@ -111,13 +113,13 @@ const ActivityLog = () => { setPage((prev) => prev + 1)} loadable={hasMore} + onLoadMore={() => setPage((prev) => prev + 1)} >
- +
@@ -132,9 +134,9 @@ const ActivityLog = () => { > diff --git a/ui/src/views/admin/activityLog/components/Log.tsx b/ui/src/views/admin/activityLog/components/Log.tsx index ada92f9..d3e2ca2 100644 --- a/ui/src/views/admin/activityLog/components/Log.tsx +++ b/ui/src/views/admin/activityLog/components/Log.tsx @@ -5,7 +5,6 @@ import { AVATAR_URL } from '@/constants/app.constant' import { NotificationDto } from '@/proxy/notification/models' import { useLocalization } from '@/utils/hooks/useLocalization' import dayjs from 'dayjs' -import { Dictionary } from 'lodash' import isEmpty from 'lodash/isEmpty' import keys from 'lodash/keys' import Event from './Event' @@ -18,7 +17,7 @@ const Log = ({ isLoading, loadable, }: { - notifications: Dictionary + notifications: Record onLoadMore: () => void isLoading: boolean loadable: boolean @@ -38,7 +37,7 @@ const Log = ({ {isEmpty(notifications[group]) ? ( Bildirim yok ) : ( - notifications[group].map((notification, i) => ( + notifications[group].map((notification) => ( {loadable ? ( - ) : ( diff --git a/ui/src/views/admin/files/FileManager.tsx b/ui/src/views/admin/files/FileManager.tsx index 09a3813..4648c43 100644 --- a/ui/src/views/admin/files/FileManager.tsx +++ b/ui/src/views/admin/files/FileManager.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef } from 'react' +import { useState, useEffect, useCallback, useRef, type MouseEvent } from 'react' import { Helmet } from 'react-helmet' import { Button, Input, Select, toast, Notification, Spinner } from '@/components/ui' import { useStoreState } from '@/store' @@ -251,7 +251,7 @@ const FileManager = () => { }) } - const handleItemDoubleClick = (item: FileItemType, event?: React.MouseEvent) => { + const handleItemDoubleClick = (item: FileItemType, event?: MouseEvent) => { // Prevent text selection and other default behaviors if (event) { event.preventDefault() @@ -409,7 +409,7 @@ const FileManager = () => { if (hasFiles) { toast.push( - Folder '{item.name}' contains files and cannot be deleted for security reasons. + Folder {item.name} contains files and cannot be deleted for security reasons. , ) return @@ -491,6 +491,8 @@ const FileManager = () => { return () => { document.removeEventListener('keydown', handleKeyDown) } + // Keyboard shortcuts should bind once per selection/list state snapshot. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedItems, filteredItems]) // Bulk operations @@ -710,7 +712,7 @@ const FileManager = () => { setLoading(false) } } - } catch (error) { + } catch { toast.push( Invalid clipboard data @@ -738,18 +740,18 @@ const FileManager = () => { @@ -869,11 +871,6 @@ const FileManager = () => { ) } - onClick={ - selectedItems.length === filteredItems.filter((item) => !item.isReadOnly).length - ? deselectAllItems - : selectAllItems - } size="sm" className="text-gray-600 hover:text-blue-600 flex-shrink-0" title={ @@ -881,6 +878,11 @@ const FileManager = () => { ? 'Deselect all selectable items' : 'Select all selectable items' } + onClick={ + selectedItems.length === filteredItems.filter((item) => !item.isReadOnly).length + ? deselectAllItems + : selectAllItems + } > {selectedItems.length === filteredItems.filter((item) => !item.isReadOnly).length @@ -904,9 +906,9 @@ const FileManager = () => { size="sm" placeholder={translate('::FileManager.SearchFiles')} value={filters.searchTerm} - onChange={(e) => setFilters((prev) => ({ ...prev, searchTerm: e.target.value }))} prefix={} className="w-full sm:w-36 md:w-48" + onChange={(e) => setFilters((prev) => ({ ...prev, searchTerm: e.target.value }))} /> @@ -954,15 +956,15 @@ const FileManager = () => { size="sm" icon={} variant={viewMode === 'grid' ? 'solid' : 'default'} - onClick={() => setViewMode('grid')} title="Grid view" + onClick={() => setViewMode('grid')} /> diff --git a/ui/src/views/admin/files/components/FileItem.tsx b/ui/src/views/admin/files/components/FileItem.tsx index a7f76d5..2764f9d 100644 --- a/ui/src/views/admin/files/components/FileItem.tsx +++ b/ui/src/views/admin/files/components/FileItem.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useState, useEffect } from 'react' +import { forwardRef, useState, useEffect, type ChangeEvent, type MouseEvent } from 'react' import classNames from 'classnames' import { FaFolder, @@ -16,7 +16,6 @@ import { FaLink, } from 'react-icons/fa' import { toast, Notification } from '@/components/ui' -// import { Dropdown } from '@/components/ui' // Artık kullanmıyoruz import type { FileItem as FileItemType, FileActionMenuItem } from '@/types/fileManagement' import { FILE_URL } from '@/constants/app.constant' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -26,7 +25,7 @@ export interface FileItemProps { selected?: boolean viewMode?: 'grid' | 'list' onSelect?: (item: FileItemType) => void - onDoubleClick?: (item: FileItemType, event?: React.MouseEvent) => void + onDoubleClick?: (item: FileItemType, event?: MouseEvent) => void onCreateFolder?: (parentItem: FileItemType) => void onRename?: (item: FileItemType) => void onMove?: (item: FileItemType) => void @@ -141,11 +140,11 @@ const FileItem = forwardRef((props, ref) => { onSelect?.(item) } - const handleDoubleClick = (e: React.MouseEvent) => { + const handleDoubleClick = (e: MouseEvent) => { onDoubleClick?.(item, e) } - const handleContextMenu = (e: React.MouseEvent) => { + const handleContextMenu = (e: MouseEvent) => { e.preventDefault() e.stopPropagation() @@ -171,12 +170,12 @@ const FileItem = forwardRef((props, ref) => { } }, [dropdownOpen]) - const handleCheckboxChange = (e: React.ChangeEvent) => { + const handleCheckboxChange = (e: ChangeEvent) => { e.stopPropagation() // Prevent item selection when clicking checkbox onToggleSelect?.(item) } - const handleCheckboxClick = (e: React.MouseEvent) => { + const handleCheckboxClick = (e: MouseEvent) => { e.stopPropagation() // Prevent item selection when clicking checkbox } @@ -216,7 +215,7 @@ const FileItem = forwardRef((props, ref) => { () => { toast.push( - Dosya URL'si panoya kopyalandı + Dosya URL'si panoya kopyalandı , ) }, @@ -367,9 +366,9 @@ const FileItem = forwardRef((props, ref) => { ) : (
// Boş alan bırak @@ -464,9 +463,9 @@ const FileItem = forwardRef((props, ref) => {
)} diff --git a/ui/src/views/admin/files/components/FileModals.tsx b/ui/src/views/admin/files/components/FileModals.tsx index ce64cb1..625320a 100644 --- a/ui/src/views/admin/files/components/FileModals.tsx +++ b/ui/src/views/admin/files/components/FileModals.tsx @@ -1,6 +1,5 @@ -import { forwardRef, useState, useEffect } from 'react' +import { forwardRef, useState, useEffect, type FormEvent } from 'react' import { Dialog, Button, Input, FormItem } from '@/components/ui' -import { FaTimes } from 'react-icons/fa' import type { FileItem } from '@/types/fileManagement' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -20,7 +19,7 @@ export const CreateFolderModal = forwardRef { + const handleSubmit = async (e: FormEvent) => { e.preventDefault() if (!folderName.trim()) { @@ -33,7 +32,7 @@ export const CreateFolderModal = forwardRef
-
+ setFolderName(e.target.value)} autoFocus + value={folderName} className="mt-2" + onChange={(e) => setFolderName(e.target.value)} />
- @@ -102,7 +101,7 @@ export const RenameItemModal = forwardRef( } }, [item]) - const handleSubmit = async (e: React.FormEvent) => { + const handleSubmit = async (e: FormEvent) => { e.preventDefault() if (!newName.trim()) { @@ -119,7 +118,7 @@ export const RenameItemModal = forwardRef( await onRename(newName.trim()) setError('') onClose() - } catch (err) { + } catch { setError(`Failed to rename ${item?.type}`) } } @@ -141,31 +140,31 @@ export const RenameItemModal = forwardRef(
-
+ setNewName(e.target.value)} - placeholder={`Enter ${translate(`::FileManager.${item.type === 'folder' ? 'FolderName' : 'FileName'}`)}`} autoFocus + value={newName} + placeholder={`Enter ${translate(`::FileManager.${item.type === 'folder' ? 'FolderName' : 'FileName'}`)}`} + onChange={(e) => setNewName(e.target.value)} />
- @@ -195,7 +194,7 @@ export const DeleteConfirmModal = forwardRef
- diff --git a/ui/src/views/admin/files/components/FileUploadModal.tsx b/ui/src/views/admin/files/components/FileUploadModal.tsx index 8e1e42c..0d48e5a 100644 --- a/ui/src/views/admin/files/components/FileUploadModal.tsx +++ b/ui/src/views/admin/files/components/FileUploadModal.tsx @@ -1,4 +1,11 @@ -import { forwardRef, useState, useCallback, useRef } from 'react' +import { + forwardRef, + useState, + useCallback, + useRef, + type ChangeEvent, + type DragEvent, +} from 'react' import { Dialog, Button, Progress } from '@/components/ui' import { FaCloudUploadAlt, FaTimes } from 'react-icons/fa' import classNames from 'classnames' @@ -22,7 +29,7 @@ interface UploadFileWithProgress { } const FileUploadModal = forwardRef((props, ref) => { - const { isOpen, onClose, onUpload, currentFolderId, loading = false, className } = props + const { isOpen, onClose, onUpload, className } = props const [uploadFiles, setUploadFiles] = useState([]) const [isDragOver, setIsDragOver] = useState(false) @@ -53,24 +60,24 @@ const FileUploadModal = forwardRef((props, setUploadFiles((prev) => [...prev, ...newFiles]) }, []) - const handleFileInputChange = (e: React.ChangeEvent) => { + const handleFileInputChange = (e: ChangeEvent) => { if (e.target.files) { handleFilesSelect(e.target.files) } } - const handleDragOver = useCallback((e: React.DragEvent) => { + const handleDragOver = useCallback((e: DragEvent) => { e.preventDefault() setIsDragOver(true) }, []) - const handleDragLeave = useCallback((e: React.DragEvent) => { + const handleDragLeave = useCallback((e: DragEvent) => { e.preventDefault() setIsDragOver(false) }, []) const handleDrop = useCallback( - (e: React.DragEvent) => { + (e: DragEvent) => { e.preventDefault() setIsDragOver(false) @@ -216,10 +223,6 @@ const FileUploadModal = forwardRef((props, } } - const clearCompletedFiles = () => { - setUploadFiles((prev) => prev.filter((f) => f.status !== 'completed')) - } - const clearErrorFiles = () => { setUploadFiles((prev) => prev.filter((f) => f.status !== 'error')) } @@ -231,7 +234,7 @@ const FileUploadModal = forwardRef((props, const hasError = errorFiles > 0 return ( - +

Upload Files

@@ -251,11 +254,11 @@ const FileUploadModal = forwardRef((props, > @@ -318,9 +321,9 @@ const FileUploadModal = forwardRef((props, variant="plain" size="sm" icon={} - onClick={() => removeFile(file.id)} disabled={uploading} className="ml-2 flex-shrink-0" + onClick={() => removeFile(file.id)} /> )}
@@ -349,21 +352,21 @@ const FileUploadModal = forwardRef((props, {hasError && !uploading && ( )} - diff --git a/ui/src/views/admin/hr/OrgChart.tsx b/ui/src/views/admin/hr/OrgChart.tsx index f02358e..3a0248a 100644 --- a/ui/src/views/admin/hr/OrgChart.tsx +++ b/ui/src/views/admin/hr/OrgChart.tsx @@ -1,4 +1,15 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { + type Dispatch, + type MutableRefObject, + type SetStateAction, + type WheelEvent, + type MouseEvent as ReactMouseEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' import { FaSitemap, FaBriefcase, @@ -19,11 +30,10 @@ import { OrgChartUserDto, } from '@/services/orgChart.service' import { useLocalization } from '@/utils/hooks/useLocalization' -import { APP_NAME } from '@/constants/app.constant' +import { AVATAR_URL, APP_NAME } from '@/constants/app.constant' import Loading from '@/components/shared/Loading' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { Avatar, Button } from '@/components/ui' -import { AVATAR_URL } from '@/constants/app.constant' import { useStoreState } from '@/store' import UserProfileCard from '@/views/intranet/SocialWall/UserProfileCard' @@ -436,9 +446,9 @@ function OrgChartNode({ tenantId?: string zoom: number positions: PositionMap - setPositions: React.Dispatch> - nodeRefs: React.MutableRefObject - cardRefs: React.MutableRefObject + setPositions: Dispatch> + nodeRefs: MutableRefObject + cardRefs: MutableRefObject requestRepaint: () => void applySelfTransform?: boolean }) { @@ -451,9 +461,8 @@ function OrgChartNode({ const levelColor = LEVEL_COLORS[depth % LEVEL_COLORS.length] const borderColor = levelColor.border const headerBg = levelColor.header - const badgeBg = levelColor.badge - const handleDragStart = (e: React.MouseEvent) => { + const handleDragStart = (e: ReactMouseEvent) => { const target = e.target as HTMLElement if (target.closest('[data-stop-drag="true"]')) { return @@ -508,12 +517,12 @@ function OrgChartNode({ ref={(el) => { cardRefs.current[node.id] = el }} - onMouseDown={handleDragStart} data-card="" data-depth={depth} data-users-visible={showUsers ? 'true' : 'false'} className={`relative bg-white border ${borderColor} rounded-xl shadow-sm w-36 sm:w-44 md:w-52 hover:shadow-md transition-shadow`} style={{ cursor: dragging ? 'grabbing' : 'grab' }} + onMouseDown={handleDragStart} > {/* Header bar */}
{ - setCollapsed((c) => !c) - requestRepaint() - }} variant="plain" shape="circle" icon={ @@ -548,6 +553,10 @@ function OrgChartNode({ ) } className="ml-1 !h-5 !w-5 flex-shrink-0 !px-0 text-white opacity-70 transition-opacity hover:opacity-100" + onClick={() => { + setCollapsed((c) => !c) + requestRepaint() + }} > )}
@@ -590,7 +599,7 @@ function OrgChartNode({ {/* Children */} {hasChildren && !collapsed && (
- {node.children.map((child, idx) => { + {node.children.map((child) => { const childPosition = positions[child.id] ?? { x: 0, y: 0 } return ( @@ -639,7 +648,7 @@ function OrgChartTree({ tenantId?: string zoom: number positions: PositionMap - setPositions: React.Dispatch> + setPositions: Dispatch> edges: EdgeLink[] }) { const containerRef = useRef(null) @@ -836,7 +845,7 @@ const OrgChart = () => { setPositions({}) }, [mode]) - const handleWheelZoom = (e: React.WheelEvent) => { + const handleWheelZoom = (e: WheelEvent) => { if (!e.ctrlKey) { return } @@ -882,7 +891,6 @@ const OrgChart = () => {
diff --git a/ui/src/views/admin/listForm/edit/ChartTabAnimation.tsx b/ui/src/views/admin/listForm/edit/ChartTabAnimation.tsx index 84cc7cc..6607aef 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabAnimation.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabAnimation.tsx @@ -1,4 +1,4 @@ -import { Card, Checkbox, FormContainer, FormItem, Input, Select, Button } from '@/components/ui' +import { Checkbox, FormContainer, FormItem, Input, Select, Button } from '@/components/ui' import { SelectBoxOption } from '@/types/shared' import { Field, FieldProps, Form, Formik } from 'formik' import { object } from 'yup' @@ -26,7 +26,7 @@ function ChartTabAnimation(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartAnimationForm, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => (
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabAnnotations.tsx b/ui/src/views/admin/listForm/edit/ChartTabAnnotations.tsx index 26ea6a8..6aac15b 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabAnnotations.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabAnnotations.tsx @@ -68,7 +68,7 @@ function ChartTabAnnotations(props: FormEditProps) { ) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => ( @@ -121,7 +121,7 @@ function ChartTabAnnotations(props: FormEditProps) { size="sm" title="Edit" icon={} - onClick={async (e) => { + onClick={async () => { setJsonRowOpModalData({ tabName: ListFormEditTabs.ChartAnnotations.GeneralJsonRow, operation: 'update', @@ -660,7 +660,7 @@ function ChartTabAnnotations(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabAxis.tsx b/ui/src/views/admin/listForm/edit/ChartTabAxis.tsx index 3538308..11e298a 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabAxis.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabAxis.tsx @@ -60,7 +60,7 @@ function ChartTabAxis(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartAxis.CommonAxisForm, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => ( @@ -114,7 +114,7 @@ function ChartTabAxis(props: FormEditProps) { size="sm" title="Edit" icon={} - onClick={async (e) => { + onClick={async () => { setJsonRowOpModalData({ tabName: ListFormEditTabs.ChartAxis.ValueAxisJsonRow, operation: 'update', @@ -541,7 +541,7 @@ function ChartTabAxis(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabCommonSettings.tsx b/ui/src/views/admin/listForm/edit/ChartTabCommonSettings.tsx index d38dd24..81d3866 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabCommonSettings.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabCommonSettings.tsx @@ -13,7 +13,7 @@ import TabList from '@/components/ui/Tabs/TabList' import TabNav from '@/components/ui/Tabs/TabNav' import { IdentityRoleDto } from '@/proxy/admin/models' import { SelectBoxOption } from '@/types/shared' -import { Field, FieldProps, Form, Formik, FormikErrors, FormikTouched } from 'formik' +import { Field, FieldProps, Form, Formik } from 'formik' import { FormEditProps } from '../../listForm/edit/FormEdit' import { useLocalization } from '@/utils/hooks/useLocalization' import { useStoreState } from '@/store/store' @@ -53,7 +53,7 @@ function ChartTabCommonSettings(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartCommon, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => ( @@ -716,7 +716,7 @@ function ChartTabCommonSettings(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabCrosshair.tsx b/ui/src/views/admin/listForm/edit/ChartTabCrosshair.tsx index e85bfd8..369df23 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabCrosshair.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabCrosshair.tsx @@ -26,7 +26,7 @@ function ChartTabCrosshair(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartCrosshairForm, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => (
@@ -336,7 +336,7 @@ function ChartTabCrosshair(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabExport.tsx b/ui/src/views/admin/listForm/edit/ChartTabExport.tsx index dc549eb..7df54a0 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabExport.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabExport.tsx @@ -1,4 +1,4 @@ -import { Button, Card, Checkbox, FormContainer, FormItem, Input } from '@/components/ui' +import { Button, Checkbox, FormContainer, FormItem, Input } from '@/components/ui' import { Field, FieldProps, Form, Formik } from 'formik' import { FormEditProps } from '../../listForm/edit/FormEdit' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -24,7 +24,7 @@ function ChartTabExport(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartExportForm, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting }) => ( @@ -65,7 +65,7 @@ function ChartTabExport(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabLegend.tsx b/ui/src/views/admin/listForm/edit/ChartTabLegend.tsx index e39769c..6b0ef91 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabLegend.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabLegend.tsx @@ -5,12 +5,8 @@ import { FormContainer, FormItem, Input, - Select, - Tabs, + Select } from '@/components/ui' -import TabContent from '@/components/ui/Tabs/TabContent' -import TabList from '@/components/ui/Tabs/TabList' -import TabNav from '@/components/ui/Tabs/TabNav' import { SelectBoxOption } from '@/types/shared' import { Field, FieldProps, Form, Formik } from 'formik' import { FormEditProps } from '../../listForm/edit/FormEdit' @@ -42,7 +38,7 @@ function ChartTabLegend(props: FormEditProps) { await props.onSubmit(ListFormEditTabs.ChartLegendForm, values, formikHelpers) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => (
@@ -216,7 +212,7 @@ function ChartTabLegend(props: FormEditProps) {
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabPanes.tsx b/ui/src/views/admin/listForm/edit/ChartTabPanes.tsx index 6be8ddb..b747ab9 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabPanes.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabPanes.tsx @@ -43,7 +43,7 @@ function ChartTabPanes(props: FormEditProps) { ) }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting }) => ( @@ -110,7 +110,7 @@ function ChartTabPanes(props: FormEditProps) { size="sm" title="Edit" icon={} - onClick={async (e) => { + onClick={async () => { setJsonRowOpModalData({ tabName: ListFormEditTabs.ChartPanes.PanesJsonRow, operation: 'update', @@ -155,7 +155,7 @@ function ChartTabPanes(props: FormEditProps) { setData={setJsonRowOpModalData} />
-
diff --git a/ui/src/views/admin/listForm/edit/ChartTabSeries.tsx b/ui/src/views/admin/listForm/edit/ChartTabSeries.tsx index 833fa02..2a9b8ac 100644 --- a/ui/src/views/admin/listForm/edit/ChartTabSeries.tsx +++ b/ui/src/views/admin/listForm/edit/ChartTabSeries.tsx @@ -34,7 +34,7 @@ import { chartSeriesSelectionModeOptions, chartSeriesDashStyleOptions, } from './options' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { JsonRowDialogData } from './json-row-operations/types' import JsonRowOpDialogSeries from './json-row-operations/JsonRowOpDialogSeries' import CreatableSelect from 'react-select/creatable' @@ -52,25 +52,22 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { const [fieldList, setFieldList] = useState([]) const listFormValues = useStoreState((s) => s.admin.lists.values) - if (!listFormValues) { - return null - } - const valueAxisList = () => { - return listFormValues?.valueAxisDto.map((key: ChartValueAxisDto) => ({ + const valueAxisList = useMemo(() => { + return (listFormValues?.valueAxisDto ?? []).map((key: ChartValueAxisDto) => ({ value: key.name, label: key.name, })) - } + }, [listFormValues?.valueAxisDto]) - const valuePaneList = () => { - return listFormValues?.panesDto.map((key: ChartPanesDto) => ({ + const valuePaneList = useMemo(() => { + return (listFormValues?.panesDto ?? []).map((key: ChartPanesDto) => ({ value: key.name, label: key.name, })) - } + }, [listFormValues?.panesDto]) - const getFields = async () => { + const getFields = useCallback(async () => { if (!props.listFormCode) { return } @@ -101,13 +98,17 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { }, ) } - } + }, [props.listFormCode]) useEffect(() => { if (props.listFormCode) { getFields() } - }, [props.listFormCode]) + }, [getFields, props.listFormCode]) + + if (!listFormValues) { + return null + } return ( - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => ( @@ -176,7 +177,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { size="sm" title="Edit" icon={} - onClick={async (e) => { + onClick={async () => { setJsonRowOpModalData({ tabName: ListFormEditTabs.ChartSeries.GeneralJsonRow, operation: 'update', @@ -346,9 +347,9 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { (option) => option.value === values.commonSeriesSettingsDto.argumentField, )} - onChange={(option) => form.setFieldValue(field.name, option?.value)} menuPlacement="auto" maxMenuHeight={150} + onChange={(option) => form.setFieldValue(field.name, option?.value)} /> )} @@ -362,7 +363,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { } errorMessage={errors.commonSeriesSettingsDto?.valueField} > - + {({ field, form }: FieldProps) => ( option.value === values.commonSeriesSettingsDto.axis, )} onChange={(option) => form.setFieldValue(field.name, option?.value)} @@ -424,9 +425,9 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) { {({ field, form }: FieldProps) => ( setTableSearch(e.target.value)} placeholder={translate('::Search') || 'Ara...'} className="w-full text-xs px-2 py-1.5 rounded border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-200 focus:outline-none focus:border-indigo-400" + onChange={(e) => setTableSearch(e.target.value)} />
@@ -117,6 +116,9 @@ function TablePickerModal({
)} @@ -248,11 +247,9 @@ function getNormalizedInitialValues(initialValues: ColumnFormatEditDto) { } function FormFieldTabLookup({ - listFormCode, initialValues, onSubmit, }: { - listFormCode: string initialValues: ColumnFormatEditDto } & FormFieldEditProps) { const { translate } = useLocalization() @@ -317,11 +314,11 @@ function FormFieldTabLookup({ extra={ @@ -465,7 +462,7 @@ function FormFieldTabLookup({
- diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabOptions.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabOptions.tsx index b91623e..4c85f0f 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabOptions.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabOptions.tsx @@ -1,5 +1,4 @@ -import { Container } from '@/components/shared' -import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui' +import { Button, Card, Checkbox, FormItem, Input, Select } from '@/components/ui' import { ColumnFormatEditDto, ListFormFieldEditTabs } from '@/proxy/admin/list-form-field/models' import { SelectBoxOption } from '@/types/shared' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -22,11 +21,9 @@ const schema = object().shape({ }) function FormFieldTabOptions({ - listFormCode, initialValues, onSubmit, }: { - listFormCode: string initialValues: ColumnFormatEditDto } & FormFieldEditProps) { const { translate } = useLocalization() @@ -113,7 +110,7 @@ function FormFieldTabOptions({
- diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPermissions.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPermissions.tsx index 8c0fcbf..cd18a7f 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPermissions.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPermissions.tsx @@ -21,11 +21,9 @@ const schema = object().shape({ }) function FormFieldTabPermissions({ - listFormCode, initialValues, onSubmit, }: { - listFormCode: string initialValues: ColumnFormatEditDto } & FormFieldEditProps) { const { translate } = useLocalization() @@ -159,7 +157,7 @@ function FormFieldTabPermissions({ - diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPivotSetting.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPivotSetting.tsx index 0d35415..6c9ace9 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPivotSetting.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabPivotSetting.tsx @@ -1,5 +1,4 @@ -import { Container } from '@/components/shared' -import { Button, Card, Checkbox, FormContainer, FormItem, Input, Select } from '@/components/ui' +import { Button, Card, Checkbox, FormItem, Select } from '@/components/ui' import { ColumnFormatEditDto, ListFormFieldEditTabs } from '@/proxy/admin/list-form-field/models' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' @@ -28,11 +27,9 @@ const schema = object().shape({ }) function FormFieldTabPivotSetting({ - listFormCode, initialValues, onSubmit, }: { - listFormCode: string initialValues: ColumnFormatEditDto } & FormFieldEditProps) { const { translate } = useLocalization() @@ -214,7 +211,7 @@ function FormFieldTabPivotSetting({
- diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabTotalSummary.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabTotalSummary.tsx index 59c3d23..9f300dc 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabTotalSummary.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabTotalSummary.tsx @@ -8,7 +8,6 @@ import { columnSummaryTypeListOptions } from '../options' import { FormFieldEditProps } from './FormFields' import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options' import { SelectBoxOption } from '@/types/shared' -import { useState } from 'react' const schema = object().shape({ columnTotalSummaryDto: object() @@ -24,12 +23,10 @@ const schema = object().shape({ }) function FormFieldTabTotalSummary({ - listFormCode, initialValues, onSubmit, fields, }: { - listFormCode: string initialValues: ColumnFormatEditDto fields: ColumnFormatEditDto[] } & FormFieldEditProps) { @@ -177,7 +174,7 @@ function FormFieldTabTotalSummary({ /> - diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabValidationRules.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabValidationRules.tsx index 3122155..aaf481f 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabValidationRules.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFieldTabValidationRules.tsx @@ -60,11 +60,9 @@ const schema = object().shape({ }) function FormFieldTabValidationRules({ - listFormCode, initialValues, onSubmit, }: { - listFormCode: string initialValues: ColumnFormatEditDto } & FormFieldEditProps) { const { translate } = useLocalization() @@ -229,7 +227,7 @@ function FormFieldTabValidationRules({ } }} > - {({ touched, errors, resetForm, values }) => ( + {({ touched, errors, values }) => (
- diff --git a/ui/src/views/admin/listForm/edit/form-fields/FormFields.tsx b/ui/src/views/admin/listForm/edit/form-fields/FormFields.tsx index 39a5013..2ac092e 100644 --- a/ui/src/views/admin/listForm/edit/form-fields/FormFields.tsx +++ b/ui/src/views/admin/listForm/edit/form-fields/FormFields.tsx @@ -35,7 +35,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import setNull from '@/utils/setNull' import classNames from 'classnames' import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { FaFileMedical, FaCopy, FaEyeSlash, FaMinus, FaTimes, FaTable } from 'react-icons/fa' import { number, object, string } from 'yup' import FormFieldEdit from './FormFieldEdit' @@ -219,7 +219,7 @@ function FormFields({ await getFields() } - const getFields = async (cultureName?: string, roleId?: string, userId?: string) => { + const getFields = useCallback(async (cultureName?: string, roleId?: string, userId?: string) => { if (!listFormCode) { return } @@ -254,11 +254,11 @@ function FormFields({ }, ) } - } + }, [listFormCode]) useEffect(() => { getFields() - }, [listFormCode]) + }, [getFields]) useEffect(() => { if (!fields || !row) { @@ -266,10 +266,10 @@ function FormFields({ } const newRow = fields.find((f) => f.id === row.id) - if (newRow) { + if (newRow && newRow !== row) { setRow(newRow) } - }, [fields]) + }, [fields, row]) const onSubmit = async ( editType: string, @@ -390,7 +390,7 @@ function FormFields({ - @@ -454,14 +454,14 @@ function FormFields({ {fields.map((field) => ( { + onClick={() => { setRow(field) }} - key={field.id} >
- +
@@ -684,7 +684,7 @@ function FormFields({ - @@ -793,7 +793,7 @@ function FormFields({ } }} > - {({ touched, errors, resetForm, isSubmitting, values }) => ( + {({ touched, errors, isSubmitting, values }) => ( - diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/EditorOptionsBuilderDialog.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/EditorOptionsBuilderDialog.tsx index 541e3aa..e34c4ac 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/EditorOptionsBuilderDialog.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/EditorOptionsBuilderDialog.tsx @@ -1,7 +1,7 @@ import { Button, Dialog } from '@/components/ui' import { useLocalization } from '@/utils/hooks/useLocalization' import { useEffect, useMemo, useState } from 'react' -import { FaCheck, FaCode, FaPlus, FaSlidersH, FaTimes, FaTrash } from 'react-icons/fa' +import { FaCheck, FaCode, FaPlus, FaTimes, FaTrash } from 'react-icons/fa' type CustomOption = { id: string @@ -295,51 +295,52 @@ function EditorOptionsBuilderDialog({ - - - @@ -451,12 +451,12 @@ function EditorOptionsBuilderDialog({ return ( @@ -545,9 +545,9 @@ function EditorOptionsBuilderDialog({ setOptionValue('width', event.target.value)} placeholder="100% veya 240" title="Örnek: 100%, 240, 80" + onChange={(event) => setOptionValue('width', event.target.value)} /> @@ -592,9 +592,9 @@ function EditorOptionsBuilderDialog({ setOptionValue('label', event.target.value)} placeholder="Alan etiketi" title="Editor içi label metni." + onChange={(event) => setOptionValue('label', event.target.value)} /> @@ -674,8 +674,8 @@ function EditorOptionsBuilderDialog({ setOptionValue( 'maxLength', event.target.value ? Number(event.target.value) : undefined, ) } - placeholder="50" - type="number" - title="Maksimum karakter sayısı." /> @@ -763,15 +763,15 @@ function EditorOptionsBuilderDialog({ setOptionValue( key, event.target.value ? Number(event.target.value) : undefined, ) } - placeholder={key === 'step' ? '1' : '0'} - type="number" - title={`${key} numeric option.`} /> ))} @@ -780,8 +780,8 @@ function EditorOptionsBuilderDialog({ setOptionPathValue( 'format.precision', event.target.value ? Number(event.target.value) : undefined, ) } - placeholder="2" - type="number" - title="Ondalık hassasiyeti." /> @@ -822,8 +822,8 @@ function EditorOptionsBuilderDialog({ setOptionValue('pickerType', event.target.value)} title="DateBox picker type." + onChange={(event) => setOptionValue('pickerType', event.target.value)} > @@ -851,9 +851,9 @@ function EditorOptionsBuilderDialog({ setOptionValue('displayFormat', event.target.value)} placeholder="dd/MM/yyyy veya HH:mm" title="DateBox display format." + onChange={(event) => setOptionValue('displayFormat', event.target.value)} /> @@ -932,6 +932,8 @@ function EditorOptionsBuilderDialog({ setCustomOptions((current) => current.map((item) => @@ -939,8 +941,6 @@ function EditorOptionsBuilderDialog({ ), ) } - placeholder="path.to.option" - title="Nokta ile nested path yaz. Örnek: toolbar.multiline" /> setCopyMappings((current) => current.map((item) => @@ -907,8 +903,6 @@ function EditorScriptBuilderDialog({ ), ) } - placeholder="Örn: unitPrice veya Customer.Name" - title="Seçilen kayıttaki kolon/path. Örnek: Quantity, UnitPrice, Customer.Name" />
@@ -1012,11 +1006,11 @@ function EditorScriptBuilderDialog({ action.operator === 'empty' || action.operator === 'notEmpty' } + placeholder="Örn: Aktif, 1000" + title="Koşulda kullanılacak sabit değer. Örnek: Aktif, 0, True" onChange={(event) => updateConditionalAction(action.id, { value: event.target.value }) } - placeholder="Örn: Aktif, 1000" - title="Koşulda kullanılacak sabit değer. Örnek: Aktif, 0, True" />
@@ -1054,7 +1048,7 @@ function EditorScriptBuilderDialog({ - + @@ -1077,11 +1071,11 @@ function EditorScriptBuilderDialog({ updateConditionalAction(action.id, { textValue: event.target.value }) } - placeholder="text, {Field} or {selected.Name}" - title="Sabit metin veya token yaz. Örnek: {Name}, {value}, {selected.DisplayName}" /> )} @@ -1129,11 +1123,11 @@ function EditorScriptBuilderDialog({ updateConditionalAction(action.id, { apiUrl: event.target.value }) } - placeholder="/api/path/{Id}" - title="Token kullanabilirsin: /api/orders/{OrderId}, /api/x/{selected.Id}, /api/y/{value}" /> @@ -1160,13 +1154,13 @@ function EditorScriptBuilderDialog({ updateConditionalAction(action.id, { textValue: event.target.value, }) } - placeholder="/report?id={Id}&type={selected.Type}" - title="Açılacak URL. Token destekler: {Id}, {value}, {selected.Type}" /> )} @@ -1209,11 +1203,11 @@ function EditorScriptBuilderDialog({ updateConditionalAction(action.id, { formula: event.target.value }) } - placeholder="Number(data.Quantity || 0) * Number(data.UnitPrice || 0)" - title="JavaScript expression yaz. data=formData, selected=selectedItem, value=e.value olarak kullanılır." /> )} @@ -1292,9 +1286,9 @@ function EditorScriptBuilderDialog({ setServiceCall(event.target.value)} placeholder="UiEvalService.ApiGenerateBackgroundWorkers();" title="Global servis çağrısı. Örnek: UiEvalService.ApiGenerateBackgroundWorkers();" + onChange={(event) => setServiceCall(event.target.value)} />
@@ -1340,7 +1334,6 @@ function EditorScriptBuilderDialog({ language="typescript" theme="vs-dark" value={scriptEditorValue} - onChange={(nextValue) => setScriptEditorValue(nextValue || '')} options={{ automaticLayout: true, fontSize: 12, @@ -1349,6 +1342,7 @@ function EditorScriptBuilderDialog({ tabSize: 2, wordWrap: 'on', }} + onChange={(nextValue) => setScriptEditorValue(nextValue || '')} />
diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx index 7cda97c..1c64b94 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogAnnotation.tsx @@ -88,10 +88,10 @@ function JsonRowOpDialogAnnotation({ id="annotationOperation" isOpen={isOpen} preventScroll={true} - onClose={handleClose} - onRequestClose={handleClose} width={900} height="90vh" + onClose={handleClose} + onRequestClose={handleClose} > {(data.operation === 'create' || data.operation === 'update') && ( (null) const [isHelperOpen, setIsHelperOpen] = useState(false) - const getFields = async () => { + const getFields = useCallback(async () => { if (!listFormCode) { return } @@ -113,7 +113,7 @@ function JsonRowOpDialogEditForm({ }, ) } - } + }, [listFormCode]) const handleClose = async (e?: any) => { if (e) { @@ -133,7 +133,7 @@ function JsonRowOpDialogEditForm({ if (isOpen && data) { getFields() } - }, [isOpen, data]) + }, [isOpen, data, getFields]) if (!data || !fieldList?.length) { return null @@ -279,10 +279,10 @@ function JsonRowOpDialogEditForm({ setIsHelperOpen(false)} - onRequestClose={() => setIsHelperOpen(false)} preventScroll={true} width={1000} + onClose={() => setIsHelperOpen(false)} + onRequestClose={() => setIsHelperOpen(false)} >
Helper
@@ -396,11 +396,11 @@ function JsonRowOpDialogEditForm({ ) : null } + menuPlacement="auto" + maxMenuHeight={150} onChange={(option) => form.setFieldValue(field.name, option?.value) } - menuPlacement="auto" - maxMenuHeight={150} /> )} @@ -422,12 +422,12 @@ function JsonRowOpDialogEditForm({ (option) => option.value === values.items?.at(index)?.editorType2, )} - onChange={(option) => - form.setFieldValue(field.name, option?.value) - } className="flex-1" menuPlacement="auto" maxMenuHeight={150} + onChange={(option) => + form.setFieldValue(field.name, option?.value) + } /> )} diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogExtraFilter.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogExtraFilter.tsx index 1f52494..8fabb7f 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogExtraFilter.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogExtraFilter.tsx @@ -10,10 +10,10 @@ import { } from '@/components/ui' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { SelectBoxOption } from '@/types/shared' -import { useStoreActions, useStoreState } from '@/store' +import { useStoreActions } from '@/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react' import { object, string } from 'yup' import { JsonRowDialogData } from './types' import { @@ -22,10 +22,10 @@ import { postListFormJsonRow, putListFormJsonRow, } from '@/services/admin/list-form.service' -import { cascadeFilterOperator, colSpanOptions } from '../options' +import { cascadeFilterOperator } from '../options' import CreatableSelect from 'react-select/creatable' import { getListFormFields } from '@/services/admin/list-form-field.service' -import { groupBy } from 'lodash' +import groupBy from 'lodash/groupBy' import { extraFilterControlTypeOptions } from '@/proxy/admin/list-form/options' const schema = object().shape({ @@ -73,7 +73,7 @@ function JsonRowOpDialogExtraFilter({ setIsOpen(false) } - const getFields = async () => { + const getFields = useCallback(async () => { if (!listFormCode) { return } @@ -104,13 +104,13 @@ function JsonRowOpDialogExtraFilter({ }, ) } - } + }, [listFormCode]) useEffect(() => { if (isOpen && data) { getFields() } - }, [isOpen, data]) + }, [isOpen, data, getFields]) if (!data || !fieldList?.length) { return null @@ -121,9 +121,9 @@ function JsonRowOpDialogExtraFilter({ id="extraFilterOperation" isOpen={isOpen} preventScroll={true} + width={data.operation === 'create' || data.operation === 'update' ? 'sm' : '450px'} onClose={handleClose} onRequestClose={handleClose} - width={data.operation === 'create' || data.operation === 'update' ? 'sm' : '450px'} > {(data.operation === 'create' || data.operation === 'update') && ( <> diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogPane.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogPane.tsx index 4016ac1..1c4565c 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogPane.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogPane.tsx @@ -5,15 +5,13 @@ import { FormItem, Input, Notification, - Select, - Tabs, toast, } from '@/components/ui' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { useStoreActions } from '@/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import { Dispatch, SetStateAction } from 'react' import { number, object, string } from 'yup' import { JsonRowDialogData } from './types' import { @@ -114,7 +112,7 @@ function JsonRowOpDialogPane({ } }} > - {({ touched, errors, values, isSubmitting }) => ( + {({ touched, errors, isSubmitting }) => (
diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx index 153ea89..a5b1613 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSeries.tsx @@ -14,7 +14,7 @@ import { SelectBoxOption } from '@/types/shared' import { useStoreActions, useStoreState } from '@/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import { Dispatch, SetStateAction } from 'react' import { boolean, number, object, string } from 'yup' import { JsonRowDialogData } from './types' import { @@ -30,7 +30,6 @@ import { columnSummaryTypeListOptions, } from '../options' import { ChartPanesDto, ChartValueAxisDto } from '@/proxy/admin/charts/models' -import CreatableSelect from 'react-select/creatable' import { tooltipFormatListOptions } from '@/proxy/admin/list-form/options' const schema = object().shape({ @@ -129,10 +128,10 @@ function JsonRowOpDialogSeries({ id="paneOperation" isOpen={isOpen} preventScroll={true} - onClose={handleClose} - onRequestClose={handleClose} width={900} height="90vh" + onClose={handleClose} + onRequestClose={handleClose} > {(data.operation === 'create' || data.operation === 'update') && ( option.value === values.argumentField, )} - onChange={(option) => form.setFieldValue(field.name, option?.value)} menuPlacement="auto" maxMenuHeight={150} + onChange={(option) => form.setFieldValue(field.name, option?.value)} /> )} @@ -303,9 +302,9 @@ function JsonRowOpDialogSeries({ isClearable={true} options={fieldList} value={fieldList?.find((option) => option.value === values.valueField)} - onChange={(option) => form.setFieldValue(field.name, option?.value)} menuPlacement="auto" maxMenuHeight={150} + onChange={(option) => form.setFieldValue(field.name, option?.value)} /> )} diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSubForm.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSubForm.tsx index e1bb0fb..770df14 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSubForm.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogSubForm.tsx @@ -122,7 +122,7 @@ function JsonRowOpDialogSubForm({ } loadData() - }, [isOpen, listFormValues?.listFormCode]) + }, [isOpen, listFormValues?.listFormCode, translate]) useEffect(() => { if (isOpen) { diff --git a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogWidget.tsx b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogWidget.tsx index 967fce5..fd976f9 100644 --- a/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogWidget.tsx +++ b/ui/src/views/admin/listForm/edit/json-row-operations/JsonRowOpDialogWidget.tsx @@ -12,10 +12,10 @@ import { import SqlEditor from '@/views/developerKit/SqlEditor' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { SelectBoxOption } from '@/types/shared' -import { useStoreActions, useStoreState } from '@/store' +import { useStoreActions } from '@/store' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik } from 'formik' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' +import { Dispatch, SetStateAction } from 'react' import { number, object, string } from 'yup' import { JsonRowDialogData } from './types' import { @@ -77,10 +77,10 @@ function JsonRowOpDialogWidget({ id="widgetOperation" isOpen={isOpen} preventScroll={true} - onClose={handleClose} - onRequestClose={handleClose} width={data.operation === 'create' || data.operation === 'update' ? 900 : 450} height="90vh" + onClose={handleClose} + onRequestClose={handleClose} > {(data.operation === 'create' || data.operation === 'update') && (
- SELECT 'Aktif' as title, COUNT(Id) as value, -
-   'blue' as color, 'FaChartBar' as icon, -
-   'Aktif kayıtlar' as subTitle -
- FROM YourTable WHERE IsActive = 1 + { + "SELECT 'Aktif' as title, COUNT(Id) as value, 'blue' as color, 'FaChartBar' as icon, 'Aktif kayıtlar' as subTitle FROM YourTable WHERE IsActive = 1" + }
@@ -269,8 +265,8 @@ function JsonRowOpDialogWidget({ > setFieldValue('sqlQuery', value || '')} height="35vh" + onChange={(value) => setFieldValue('sqlQuery', value || '')} /> diff --git a/ui/src/views/admin/listForm/wizard/Wizard.tsx b/ui/src/views/admin/listForm/wizard/Wizard.tsx index b8012f7..2aca572 100644 --- a/ui/src/views/admin/listForm/wizard/Wizard.tsx +++ b/ui/src/views/admin/listForm/wizard/Wizard.tsx @@ -10,7 +10,6 @@ import { useLocation, useNavigate } from 'react-router-dom' import * as Yup from 'yup' import { getMenus } from '@/services/menu.service' import { getPermissions } from '@/services/identity.service' -import { DbTypeEnum, SelectCommandTypeEnum } from '@/proxy/form/models' import { getDataSources } from '@/services/data-source.service' import { sqlObjectManagerService } from '@/services/sql-query-manager.service' import type { SqlObjectExplorerDto, DatabaseColumnDto } from '@/proxy/sql-query-manager/models' @@ -32,13 +31,15 @@ import { Container } from '@/components/shared' import { sqlDataTypeToDbType } from '../edit/options' import { useStoreActions } from '@/store/store' import { deleteWizardFile, getWizardFile, postListFormWizard } from '@/services/wizard.service' -import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models' import { ListFormWizardDto } from '@/proxy/admin/wizard/models' import { ListFormWorkflowCriteriaDto, SubFormDto, WidgetEditDto, WorkflowDto, + UiLookupDataSourceTypeEnum, + DbTypeEnum, + SelectCommandTypeEnum, } from '@/proxy/form/models' // ─── Formik initial values & validation ────────────────────────────────────── @@ -271,7 +272,9 @@ const Wizard = () => { setSelectCommandColumns(cols) const colNames = new Set(cols.map((c) => c.columnName.toLowerCase())) const hasTenantColumn = colNames.has('tenantid') - const selectableColumns = cols.filter((c) => isAutoSelectedColumn(c.columnName, hasTenantColumn)) + const selectableColumns = cols.filter((c) => + isAutoSelectedColumn(c.columnName, hasTenantColumn), + ) setSelectedColumns(new Set(selectableColumns.map((c) => c.columnName))) setEditingGroups([]) // Auto-check isTenant / isBranch based on column presence @@ -297,7 +300,11 @@ const Wizard = () => { const toggleColumn = (col: string) => setSelectedColumns((prev) => { const next = new Set(prev) - next.has(col) ? next.delete(col) : next.add(col) + if (next.has(col)) { + next.delete(col) + } else { + next.add(col) + } return next }) @@ -548,15 +555,17 @@ const Wizard = () => { const previousSpacedLabel = toSpacedLabel(formik?.values.wizardName ?? '') const derived = deriveListFormCode(name) - const setAutoText = (field: keyof Pick< - ListFormWizardDto, - | 'languageTextMenuEn' - | 'languageTextMenuTr' - | 'languageTextTitleEn' - | 'languageTextTitleTr' - | 'languageTextDescEn' - | 'languageTextDescTr' - >) => { + const setAutoText = ( + field: keyof Pick< + ListFormWizardDto, + | 'languageTextMenuEn' + | 'languageTextMenuTr' + | 'languageTextTitleEn' + | 'languageTextTitleTr' + | 'languageTextDescEn' + | 'languageTextDescTr' + >, + ) => { const current = formik?.values[field] if (!current || current === previousSpacedLabel) { formik?.setFieldValue(field, spacedLabel) @@ -789,7 +798,7 @@ const Wizard = () => { setSubmitting(false) }} > - {({ touched, errors, isSubmitting, values }) => ( + {({ touched, errors, values }) => ( = 2 ? undefined : 'sm'}> {/* ─── Step 1: Basic Info ─────────────────────────────── */} @@ -856,7 +865,6 @@ const Wizard = () => { groups={editingGroups} onGroupsChange={setEditingGroups} dbObjects={dbObjects} - isLoadingDbObjects={isLoadingDbObjects} dsCode={currentDataSource} translate={translate} onBack={() => setCurrentStep(1)} diff --git a/ui/src/views/admin/listForm/wizard/WizardStep1.tsx b/ui/src/views/admin/listForm/wizard/WizardStep1.tsx index 3193b78..0035db5 100644 --- a/ui/src/views/admin/listForm/wizard/WizardStep1.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardStep1.tsx @@ -321,7 +321,8 @@ function MenuTreeInline({ const toggle = (code: string) => setExpanded((prev) => { const n = new Set(prev) - n.has(code) ? n.delete(code) : n.add(code) + if (n.has(code)) n.delete(code) + else n.add(code) return n }) diff --git a/ui/src/views/admin/listForm/wizard/WizardStep3.tsx b/ui/src/views/admin/listForm/wizard/WizardStep3.tsx index 6be2369..dea2185 100644 --- a/ui/src/views/admin/listForm/wizard/WizardStep3.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardStep3.tsx @@ -64,7 +64,6 @@ export interface WizardStep3Props { groups: WizardGroup[] onGroupsChange: (groups: WizardGroup[]) => void dbObjects: SqlObjectExplorerDto | null - isLoadingDbObjects: boolean dsCode: string translate: (key: string) => string onBack: () => void @@ -794,7 +793,6 @@ const WizardStep3 = ({ groups, onGroupsChange, dbObjects, - isLoadingDbObjects: _isLoadingDbObjects, dsCode, translate, onBack, diff --git a/ui/src/views/admin/listForm/wizard/WizardStep5.tsx b/ui/src/views/admin/listForm/wizard/WizardStep5.tsx index 1fe3756..37bf1f2 100644 --- a/ui/src/views/admin/listForm/wizard/WizardStep5.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardStep5.tsx @@ -239,10 +239,10 @@ function WizardStep5({ widgets, translate, onChange, onBack, onNext }: Props) { title={
SQL Query Examples:
- - SELECT 'Aktif' as title, COUNT(Id) as value, 'blue' as color, 'FaChartBar' - as icon, 'Aktif kayitlar' as subTitle FROM YourTable - + +
+                          {`SELECT 'Aktif' as title, COUNT(Id) as value, 'blue' as color, 'FaChartBar' as icon, 'Aktif kayitlar' as subTitle FROM YourTable`}
+                        
} > diff --git a/ui/src/views/admin/listForm/wizard/WizardStep6.tsx b/ui/src/views/admin/listForm/wizard/WizardStep6.tsx index 75bb89f..f0bfe2c 100644 --- a/ui/src/views/admin/listForm/wizard/WizardStep6.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardStep6.tsx @@ -45,7 +45,7 @@ const toDesignerCriteria = (items: ListFormWorkflowCriteriaDto[]): WorkflowCrite })) const toWizardCriteria = (items: WorkflowCriteriaDto[]): ListFormWorkflowCriteriaDto[] => - items.map(({ nodeId: _nodeId, ...item }) => item) + items.map(({ ...item }) => item) function WizardStep6({ listFormCode, diff --git a/ui/src/views/admin/listForm/wizard/WizardStep7.tsx b/ui/src/views/admin/listForm/wizard/WizardStep7.tsx index 8efced2..44685fa 100644 --- a/ui/src/views/admin/listForm/wizard/WizardStep7.tsx +++ b/ui/src/views/admin/listForm/wizard/WizardStep7.tsx @@ -1,6 +1,6 @@ import { Button } from '@/components/ui' import type { DatabaseColumnDto } from '@/proxy/sql-query-manager/models' -import { useState } from 'react' +import React, { useState } from 'react' import { FaArrowLeft, FaCheckCircle, diff --git a/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx b/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx index ad35170..65e2ac1 100644 --- a/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx +++ b/ui/src/views/admin/listForm/workflow/WorkflowCanvas.tsx @@ -217,7 +217,6 @@ export function WorkflowCanvas({ @@ -254,7 +253,6 @@ export function WorkflowCanvas({ ))} @@ -292,6 +290,7 @@ function FlowNode({ return ( @@ -440,8 +443,8 @@ export function WorkflowCriteria({ @@ -508,14 +511,14 @@ function SelectField({ undefined} /> diff --git a/ui/src/views/admin/listForm/workflow/WorkflowDesigner.tsx b/ui/src/views/admin/listForm/workflow/WorkflowDesigner.tsx index 03dfce1..5950387 100644 --- a/ui/src/views/admin/listForm/workflow/WorkflowDesigner.tsx +++ b/ui/src/views/admin/listForm/workflow/WorkflowDesigner.tsx @@ -82,8 +82,6 @@ export function WorkflowDesigner({ onZoomIn, onZoomOut, }: WorkflowDesignerProps) { - const { translate } = useLocalization() - return (
diff --git a/ui/src/views/admin/notification/CreateNotification.tsx b/ui/src/views/admin/notification/CreateNotification.tsx index 19eea4b..d2ac762 100644 --- a/ui/src/views/admin/notification/CreateNotification.tsx +++ b/ui/src/views/admin/notification/CreateNotification.tsx @@ -10,7 +10,6 @@ import { import { postMyNotificationByNotificationRuleId } from '@/services/notification.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, Form, Formik, FormikHelpers } from 'formik' -import { useState } from 'react' import * as Yup from 'yup' export interface NotificationDto { @@ -33,7 +32,6 @@ function CreateNotification({ id: string }) { const { translate } = useLocalization() - const [loading, setLoading] = useState(true) const handleSubmit = async ( values: NotificationDto, @@ -43,7 +41,6 @@ function CreateNotification({ return } - setLoading(true) setSubmitting(true) try { @@ -57,7 +54,7 @@ function CreateNotification({ }, ) onDialogClose() - } catch (error) { + } catch { toast.push( {'Hata'} @@ -67,7 +64,6 @@ function CreateNotification({ }, ) } finally { - setLoading(false) setSubmitting(false) } } @@ -76,7 +72,7 @@ function CreateNotification({
{id}
- {({ touched, errors, values, isSubmitting }) => { + {({ touched, errors, isSubmitting }) => { return ( diff --git a/ui/src/views/admin/profile/Profile.tsx b/ui/src/views/admin/profile/Profile.tsx index e5852e2..f970e6b 100644 --- a/ui/src/views/admin/profile/Profile.tsx +++ b/ui/src/views/admin/profile/Profile.tsx @@ -3,31 +3,17 @@ import Container from '@/components/shared/Container' import Tabs from '@/components/ui/Tabs' import { APP_NAME } from '@/constants/app.constant' import { useLocalization } from '@/utils/hooks/useLocalization' -import { Suspense, lazy, useState } from 'react' +import { Suspense, lazy, type ReactNode } from 'react' import { Helmet } from 'react-helmet' -import { FaCheckCircle, FaCompressAlt, FaNode, FaUser } from 'react-icons/fa' +import { FaCheckCircle, FaCompressAlt, FaUser } from 'react-icons/fa' import { useLocation, useNavigate } from 'react-router-dom' -type AccountSetting = { - profile: { - firstName?: string - lastName?: string - avatar?: string - lang?: string - } - loginHistory: { - type: string - deviceName: string - time: number - location: string - }[] -} - const General = lazy(() => import('./components/General')) const Password = lazy(() => import('./components/Password')) const NotificationSettings = lazy(() => import('./components/NotificationSettings')) const { TabNav, TabList } = Tabs +const PROFILE_BASE_PATH = '/admin/profile' const Profile = () => { const { translate } = useLocalization() @@ -36,7 +22,7 @@ const Profile = () => { { label: string path: string - icon?: JSX.Element + icon?: ReactNode } > = { general: { @@ -58,15 +44,12 @@ const Profile = () => { const location = useLocation() const path = location.pathname.substring(location.pathname.lastIndexOf('/') + 1) - const [currentTab, setCurrentTab] = useState(path) - const [data, setData] = useState>({}) const navigate = useNavigate() - - const PROFILE_BASE_PATH = '/admin/profile' + const currentTab = + Object.entries(settingsMenu).find(([, setting]) => setting.path === path)?.[0] ?? 'general' const onTabChange = (val: string) => { if (settingsMenu[val]) { - setCurrentTab(settingsMenu[val].path) navigate(`${PROFILE_BASE_PATH}/${settingsMenu[val].path}`) } } @@ -94,8 +77,8 @@ const Profile = () => {
}> {currentTab === 'general' && } - {currentTab === 'password' && } - {currentTab === 'notification-settings' && } + {currentTab === 'password' && } + {currentTab === 'notificationSettings' && }
diff --git a/ui/src/views/admin/profile/components/General.tsx b/ui/src/views/admin/profile/components/General.tsx index 4953639..3a108c3 100644 --- a/ui/src/views/admin/profile/components/General.tsx +++ b/ui/src/views/admin/profile/components/General.tsx @@ -9,7 +9,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import dayjs from 'dayjs' import { Field, Form, Formik } from 'formik' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { CircleStencil, Cropper, @@ -29,7 +29,6 @@ import { FaUniversity, } from 'react-icons/fa' import * as Yup from 'yup' -import isEmpty from 'lodash/isEmpty' import { ProfileDto, UpdateProfileDto } from '@/proxy/account/models' import { getProfile, updateProfile } from '@/services/account.service' import { CountryDto, getCountry } from '@/services/home.service' @@ -58,7 +57,7 @@ const General = () => { const cropperRef = useRef(null) const previewRef = useRef(null) - const fetchData = async () => { + const fetchData = useCallback(async () => { setLoading(true) const [response, countryResponse] = await Promise.all([getProfile(), getCountry()]) setProfileData(response.data) @@ -69,13 +68,11 @@ const General = () => { surname: response.data.surname, }) setLoading(false) - } + }, [auth.user.avatar]) useEffect(() => { - if (isEmpty(profileData)) { - fetchData() - } - }, []) + fetchData() + }, [fetchData]) const onChooseImage = async (file: File[]) => { if (file[0]) { @@ -282,16 +279,16 @@ const General = () => { } value={profileData?.phoneNumber} > } value={profileData?.extraProperties?.['RocketUsername'] as string | undefined} > @@ -305,8 +302,8 @@ const General = () => { } value={getProfileExtraValue('HomeAddress')} > @@ -333,8 +330,8 @@ const General = () => { } value={getProfileExtraValue('GraduationSchool')} > @@ -357,15 +354,15 @@ const General = () => { { setTimeout(() => { previewRef.current?.update(cropper) }, 100) }} - className="cropper max-h-[300px] max-w-[300px]" - stencilComponent={CircleStencil} - minHeight={100} - minWidth={100} /> ) : ( { const [notificationTypes, setNotificationTypes] = useState([]) const [channels, setChannels] = useState([]) - const fetchData = async () => { + const fetchData = useCallback(async () => { setLoading(true) const channels = Object.keys(NotificationChannels) setChannels(channels) @@ -34,7 +32,7 @@ const NotificationSettings = () => { const x: NotificationRuleDto[] = [] for (const channel of channels) { const rule = rules.find( - (a) => a.channel == channel && a.notificationType == notificationType, + (a) => a.channel === channel && a.notificationType === notificationType, ) x.push( rule ?? @@ -49,13 +47,11 @@ const NotificationSettings = () => { } setList(finalList) setLoading(false) - } + }, []) useEffect(() => { - if (isEmpty(list)) { - fetchData() - } - }, []) + fetchData() + }, [fetchData]) const onClickCheckbox = async (checked: boolean, rule: NotificationRuleDto) => { await postMyNotificationRule({ @@ -63,18 +59,18 @@ const NotificationSettings = () => { notificationType: rule.notificationType, isActive: checked, }) - fetchData() + await fetchData() } const resetMyNotifications = async (notificationType: string) => { await deleteMyNotificationRules(notificationType) - fetchData() + await fetchData() } return ( <> {loading ? ( -
+
) : ( @@ -115,8 +111,8 @@ const NotificationSettings = () => { name={a + b} disabled={list[m][n].isFixed} checked={list[m][n].isActive} - onChange={(e) => onClickCheckbox(e, list[m][n])} color={list[m][n].isCustomized ? 'red-500' : undefined} + onChange={(e) => onClickCheckbox(e, list[m][n])} > ))} diff --git a/ui/src/views/admin/profile/components/Password.tsx b/ui/src/views/admin/profile/components/Password.tsx index 16e247c..0eb9612 100644 --- a/ui/src/views/admin/profile/components/Password.tsx +++ b/ui/src/views/admin/profile/components/Password.tsx @@ -8,35 +8,14 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import FormDesription from '@/views/shared/FormDesription' import FormRow from '@/views/shared/FormRow' import { Field, Form, Formik } from 'formik' -import { FaDesktop, FaMobileAlt, FaTabletAlt } from 'react-icons/fa' import * as Yup from 'yup' -type LoginHistory = { - type: string - deviceName: string - time: number - location: string -} - type PasswordFormModel = { password: string newPassword: string confirmNewPassword: string } -const LoginHistoryIcon = ({ type }: { type: string }) => { - switch (type) { - case 'Desktop': - return - case 'Mobile': - return - case 'Tablet': - return - default: - return - } -} - const validationSchema = Yup.object().shape({ password: Yup.string().required(), newPassword: Yup.string() @@ -49,7 +28,7 @@ const validationSchema = Yup.object().shape({ confirmNewPassword: Yup.string().oneOf([Yup.ref('newPassword'), ''], 'Password not match'), }) -const Password = ({ data }: { data?: LoginHistory[] }) => { +const Password = () => { const onFormSubmit = async ( values: PasswordFormModel, setSubmitting: (isSubmitting: boolean) => void, @@ -144,48 +123,8 @@ const Password = ({ data }: { data?: LoginHistory[] }) => { ) - }} + }} - {/*
- - {data && ( -
- {data.map((log, index) => ( -
-
-
- -
-
-
-
- {log.deviceName} -
- {index === 0 && ( - - Current - - )} -
- - {log.location} • {dayjs.unix(log.time).format('DD-MMM-YYYY, hh:mm A')} - -
-
-
- ))} -
- )} -
*/} ) } diff --git a/ui/src/views/admin/role-management/RolesPermission.tsx b/ui/src/views/admin/role-management/RolesPermission.tsx index 9818b14..aa21206 100644 --- a/ui/src/views/admin/role-management/RolesPermission.tsx +++ b/ui/src/views/admin/role-management/RolesPermission.tsx @@ -24,7 +24,6 @@ function PermissionDialogContent({ translate, permissionList, selectedGroup, - selectedGroupPermissions, filteredPermissions, isAllSelected, isAllSelectedForGroup, @@ -40,7 +39,6 @@ function PermissionDialogContent({ changeGroup, togglePermission, isParent, - getChildren, onClickCheckbox, setSearchTerm, setCopyDialogOpen, @@ -488,6 +486,7 @@ function RolesPermission({ const res = await getRoles() setRoleList(res.data.items?.map((r: any) => r.name) || []) } catch (e) { + console.error('Failed to fetch roles:', e) setRoleList([]) } } @@ -515,6 +514,7 @@ function RolesPermission({ setCopyDialogOpen(false) setCopyDialogRole('') } catch (e) { + console.error('Failed to copy permissions:', e) toast.push(, { placement: 'top-end', }) diff --git a/ui/src/views/admin/tenant-management/TenantsConnectionString.tsx b/ui/src/views/admin/tenant-management/TenantsConnectionString.tsx index 95ee1a3..985d7f9 100644 --- a/ui/src/views/admin/tenant-management/TenantsConnectionString.tsx +++ b/ui/src/views/admin/tenant-management/TenantsConnectionString.tsx @@ -20,7 +20,7 @@ import { } from '@/services/tenant.service' import { useLocalization } from '@/utils/hooks/useLocalization' import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik' -import { Suspense, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useState, type MouseEvent } from 'react' import * as Yup from 'yup' import { dataSourceTypeOptions } from '../listForm/edit/options' import { DataSourceTypeEnum } from '@/proxy/form/models' @@ -60,25 +60,15 @@ function TenantConnectionString({ }) { const { translate } = useLocalization() const [currentTab, setCurrentTab] = useState('cs') - const [loading, setLoading] = useState(true) const [connectionString, setConnectionString] = useState() - const fetchDataTenants = async () => { + const fetchDataTenants = useCallback(async () => { if (!id) { return } - setLoading(true) const response = await getTenantDefaultConnectionString(id) - let value = '' - let isNew = false - - if (response.data.length == 0) { - isNew = true - value = '' - } else { - isNew = false - value = response.data - } + const isNew = response.data.length === 0 + const value = isNew ? '' : response.data setConnectionString({ id, @@ -87,19 +77,17 @@ function TenantConnectionString({ isNew, ...(value && { dataSourceType: value.startsWith('Server') ? 1 : 2 }), }) - setLoading(false) - } + }, [id]) useEffect(() => { + setConnectionString(undefined) fetchDataTenants() - }, [id]) + }, [fetchDataTenants]) const deleteConnectionString = async () => { if (!id) { return } - setLoading(true) - try { await deleteTenantDefaultConnectionString(id) toast.push( @@ -111,7 +99,7 @@ function TenantConnectionString({ }, ) onDialogClose() - } catch (error) { + } catch { toast.push( {'Hata'} @@ -120,8 +108,6 @@ function TenantConnectionString({ placement: 'top-end', }, ) - } finally { - setLoading(false) } } @@ -133,7 +119,6 @@ function TenantConnectionString({ return } - setLoading(true) setSubmitting(true) try { @@ -148,8 +133,7 @@ function TenantConnectionString({ ) await fetchDataTenants() - //onDialogClose() - } catch (error) { + } catch { toast.push( {'Hata'} @@ -159,20 +143,19 @@ function TenantConnectionString({ }, ) } finally { - setLoading(false) setSubmitting(false) } } const [pwInputType, setPwInputType] = useState('password') - const onPasswordVisibleClick = (e: React.MouseEvent) => { + const onPasswordVisibleClick = (e: MouseEvent) => { e.preventDefault() setPwInputType(pwInputType === 'password' ? 'text' : 'password') } const passwordVisible = ( - onPasswordVisibleClick(e as any)}> + {pwInputType === 'password' ? : } ) @@ -185,7 +168,6 @@ function TenantConnectionString({ return } - setLoading(true) setSubmitting(true) try { @@ -216,7 +198,6 @@ function TenantConnectionString({ }, ) } finally { - setLoading(false) setSubmitting(false) } } @@ -243,177 +224,175 @@ function TenantConnectionString({
- }> - {currentTab === 'cs' && ( - - {({ touched, errors, values, isSubmitting }) => { - return ( -
- - + {({ touched, errors, values, isSubmitting }) => { + return ( + + + + + + + + - - + {({ field, form }: FieldProps) => ( + option.value === values.dataSourceType, - )} - onChange={(option) => { - form.setFieldValue(field.name, option?.value) + {translate('::Delete')} + + + +
+ + + ) + }} + + )} + {currentTab === 'seed' && ( + + {({ touched, errors, isSubmitting }) => { + return ( +
+ + + + - if (option?.value == 1) - //PostgreSQL - form.setFieldValue( - 'value', - 'Server=sql;Database=' + - name + - ';User Id=sa;password=@Password;Trusted_Connection=False;TrustServerCertificate=True;Connection Timeout=60;', - ) - else if (option?.value == 2) - //MsSql - form.setFieldValue( - 'value', - 'User ID=sa;Password=@Password;Host=postgres;Port=5432;Database=' + - name + - ';', - ) - }} - /> - )} - - + + + - - - - -
- - - -
-
-
- ) - }} -
- )} - {currentTab === 'seed' && ( - - {({ touched, errors, values, isSubmitting }) => { - return ( -
- - - - - - - - - -
- - -
-
-
- ) - }} -
- )} - +
+ + +
+ + + ) + }} + + )}
) diff --git a/ui/src/views/admin/user-management/Details.tsx b/ui/src/views/admin/user-management/Details.tsx index 0d109d6..cf30c7d 100644 --- a/ui/src/views/admin/user-management/Details.tsx +++ b/ui/src/views/admin/user-management/Details.tsx @@ -77,7 +77,7 @@ import 'react-advanced-cropper/dist/style.css' import { useParams } from 'react-router-dom' import * as Yup from 'yup' import { SelectBoxOption, SelectBoxOptionWithGroup } from '@/types/shared' -import { AdaptableCard, ConfirmDialog, Container } from '@/components/shared' +import { AdaptableCard, ConfirmDialog } from '@/components/shared' import { AssignedClaimViewModel, UserInfoViewModel } from '@/proxy/admin/models' import { APP_NAME, AVATAR_URL } from '@/constants/app.constant' import { useStoreActions, useStoreState } from '@/store' @@ -92,7 +92,6 @@ function UserDetails() { const { userId } = useParams() const { translate } = useLocalization() const [userDetails, setUserDetails] = useState() - const [loading, setLoading] = useState(true) const [open, setOpen] = useState(false) const [confirmDeleteClaim, setConfirmDeleteClaim] = useState(null) const [countries, setCountries] = useState([]) @@ -106,7 +105,6 @@ function UserDetails() { const { setting } = useSetting() const isEmailUpdateEnabled = setting('Abp.Identity.User.IsEmailUpdateEnabled') - const isRequireVerifiedAccount = setting('Abp.Identity.Profile.General.RequireVerifiedAccount') const isRequireConfirmedEmail = setting('Abp.Identity.SignIn.RequireConfirmedEmail') const isRequireConfirmedPhoneNumber = setting('Abp.Identity.SignIn.RequireConfirmedPhoneNumber') const isTwoFactorEnabled = setting('Abp.Account.TwoFactor.Enabled') @@ -198,7 +196,6 @@ function UserDetails() { values: ClaimTypeDto, { setSubmitting }: FormikHelpers, ) => { - setLoading(true) setSubmitting(true) try { @@ -213,7 +210,7 @@ function UserDetails() { ) setOpen(false) getUser() - } catch (error) { + } catch { toast.push( {'Hata'} @@ -223,7 +220,6 @@ function UserDetails() { }, ) } finally { - setLoading(false) setSubmitting(false) } } @@ -262,7 +258,7 @@ function UserDetails() { { + onSubmit={async (values, { setSubmitting }) => { setSubmitting(true) await putUserDetail({ ...values }) if (values.id === auth.user.id) { @@ -643,7 +639,7 @@ function UserDetails() {
- {({ form, remove, push }) => ( + {() => (
{branches && branches.length > 0 ? branches.map((_, index: number) => ( @@ -675,7 +671,7 @@ function UserDetails() {
- {({ form, remove, push }) => ( + {() => (
{roles && roles.length > 0 ? roles.map((_, index: number) => ( @@ -720,7 +716,7 @@ function UserDetails() { { + onSubmit={async (values, { setSubmitting }) => { setSubmitting(true) await putUserDetail({ ...values }) @@ -902,7 +898,7 @@ function UserDetails() { { + onSubmit={async (values, { setSubmitting }) => { setSubmitting(true) await putUserDetail({ ...values }) @@ -1655,7 +1651,7 @@ function UserDetails() { size="sm" title="Add" icon={} - onClick={async (e) => { + onClick={() => { setOpen(true) }} /> @@ -1665,25 +1661,26 @@ function UserDetails() { - {userDetails.claims.filter((a) => a.isAssigned === true) && - userDetails.claims.filter((a) => a.isAssigned === true).length > 0 ? ( - userDetails.claims.map((claim) => ( - - -
diff --git a/ui/src/views/developerKit/ComponentEditor.tsx b/ui/src/views/developerKit/ComponentEditor.tsx index 4bb858c..db097f3 100644 --- a/ui/src/views/developerKit/ComponentEditor.tsx +++ b/ui/src/views/developerKit/ComponentEditor.tsx @@ -1,31 +1,14 @@ -import React, { useState, useEffect, useCallback } from 'react' +import React, { useState, useEffect } from 'react' import { useParams, useNavigate, Link } from 'react-router-dom' import { useComponents } from '../../contexts/ComponentContext' -import { - FaRegSave, - FaArrowLeft, - FaExclamationCircle, - FaSync, - FaCode, - FaEye, - FaCog, -} from 'react-icons/fa' -import { parseReactCode } from '../../utils/codeParser' +import { FaRegSave, FaArrowLeft, FaSync, FaCode, FaEye, FaCog } from 'react-icons/fa' import ComponentPreview from '../../components/componentEditor/ComponentPreview' -import { EditorState } from '../../proxy/developerKit/componentInfo' import { ROUTES_ENUM } from '@/routes/route.constant' import { useLocalization } from '@/utils/hooks/useLocalization' -import { Formik, Form, Field, FieldProps } from 'formik' +import { Formik, Form, Field } from 'formik' import * as Yup from 'yup' import { Button, Checkbox, FormContainer, FormItem, Input } from '@/components/ui' -// Error tipini tanımla -interface ValidationError { - message: string - severity: number - startLineNumber?: number -} - // Validation schema const validationSchema = Yup.object({ name: Yup.string().required(), @@ -42,18 +25,10 @@ const ComponentEditor: React.FC = () => { const { getComponent, addComponent, updateComponent } = useComponents() - const [validationErrors, setValidationErrors] = useState([]) const [isLoaded, setIsLoaded] = useState(false) const isEditing = !!id - //Eksikler Dahil edit modunda - const [editorState, setEditorState] = useState({ - code: '', - components: [], - selectedComponentId: null, - }) - // Initial values for Formik const [initialValues, setInitialValues] = useState({ name: '', @@ -63,24 +38,6 @@ const ComponentEditor: React.FC = () => { isActive: true, }) - const parseAndUpdateComponents = useCallback((code: string) => { - try { - const parsed = parseReactCode(code) - setEditorState((prev) => ({ - ...prev, - code, - components: parsed.components, - })) - } catch (error) { - const msg = error instanceof Error ? error.message : 'Koddan komponentler parse edilemedi.' - console.log('Parse error:', msg) - setEditorState((prev) => ({ - ...prev, - code, // Only update the code, keep existing components - })) - } - }, []) - // Load existing component data - sadece edit modunda useEffect(() => { if (isEditing && id && !isLoaded) { @@ -104,14 +61,13 @@ const ComponentEditor: React.FC = () => { } setInitialValues(values) - parseAndUpdateComponents(component.code) setIsLoaded(true) } } else if (!isEditing && !isLoaded) { // Yeni komponent için boş başla - TEMPLATE YOK setIsLoaded(true) } - }, [id, isEditing, getComponent, isLoaded, parseAndUpdateComponents]) + }, [id, isEditing, getComponent, isLoaded]) // Generate component template based on name const generateComponentTemplate = (componentName: string): string => { @@ -138,27 +94,6 @@ export default ${pascalCaseName}Component;` return } - const criticalErrors = validationErrors.filter((error) => { - const message = error.message.toLowerCase() - return ( - !message.includes('can only be used in typescript files') && - !message.includes('type annotations can only be used') && - !message.includes('jsx-runtime') && - !message.includes('corresponding type declarations') && - error.severity === 8 - ) - }) - - if (criticalErrors.length > 0) { - const proceed = window.confirm( - `There are ${criticalErrors.length} critical error(s) in your code. Do you want to save anyway?`, - ) - if (!proceed) { - setSubmitting(false) - return - } - } - try { const componentData = { name: values.name.trim(), @@ -221,7 +156,9 @@ export default ${pascalCaseName}Component;` : translate('::App.DeveloperKit.ComponentEditor.Title.Create')}

- {isEditing ? 'Modify your React component' : 'Create a new React component'} + {isEditing + ? 'Modify your React component' + : 'Create a new React component'}

@@ -240,11 +177,11 @@ export default ${pascalCaseName}Component;`
- {/* Right Side - Preview and Validation */} + {/* Right Side - Preview */}
- {/* Validation Errors */} - {validationErrors.length > 0 && ( -
-
-
- -
-
-

- Validation Issues -

-

- {validationErrors.length} issue - {validationErrors.length !== 1 ? 's' : ''} found in your code -

-
- {validationErrors.slice(0, 5).map((error, index) => ( -
-
- - Line {error.startLineNumber} - - {error.message} -
-
- ))} - {validationErrors.length > 5 && ( -
- ... and {validationErrors.length - 5} more issue - {validationErrors.length - 5 !== 1 ? 's' : ''} -
- )} -
-
-
-
- )} - {/* Component Preview */}
diff --git a/ui/src/views/developerKit/ComponentManager.tsx b/ui/src/views/developerKit/ComponentManager.tsx index 991dd1a..c4078a8 100644 --- a/ui/src/views/developerKit/ComponentManager.tsx +++ b/ui/src/views/developerKit/ComponentManager.tsx @@ -55,7 +55,7 @@ const ComponentManager: React.FC = () => { } } - const handleDelete = async (id: string, name: string) => { + const handleDelete = async (id: string) => { if (window.confirm(translate('::App.DeveloperKit.Component.ConfirmDelete'))) { try { await deleteComponent(id) @@ -221,13 +221,13 @@ const ComponentManager: React.FC = () => { > {component.isActive ? ( <> - - {translate('::App.DeveloperKit.Component.Active')} + + {translate('::App.Status.Active')} ) : ( <> - - {translate('::App.DeveloperKit.Component.Inactive')} + + {translate('::App.Status.Inactive')} )} @@ -249,7 +249,7 @@ const ComponentManager: React.FC = () => { } title={translate('::App.DeveloperKit.Component.Edit')} > - +
diff --git a/ui/src/views/developerKit/CrudEndpointManager.tsx b/ui/src/views/developerKit/CrudEndpointManager.tsx index 563ce52..38bf51f 100644 --- a/ui/src/views/developerKit/CrudEndpointManager.tsx +++ b/ui/src/views/developerKit/CrudEndpointManager.tsx @@ -2,7 +2,6 @@ import axios from 'axios' import { FaSearch, - FaGlobe, FaCopy, FaCheckCircle, FaExclamationCircle, @@ -16,7 +15,6 @@ import { FaToggleOff, FaChevronRight, FaChevronDown, - FaServer, } from 'react-icons/fa' import { useLocalization } from '@/utils/hooks/useLocalization' import Widget from '@/components/common/Widget' diff --git a/ui/src/views/developerKit/DynamicServiceEditor.tsx b/ui/src/views/developerKit/DynamicServiceEditor.tsx index 89949e2..85a106d 100644 --- a/ui/src/views/developerKit/DynamicServiceEditor.tsx +++ b/ui/src/views/developerKit/DynamicServiceEditor.tsx @@ -7,7 +7,6 @@ import { FaCheckCircle, FaExclamationCircle, FaSpinner, - FaExternalLinkAlt, FaArrowLeft, FaCog, FaCode, diff --git a/ui/src/views/developerKit/SqlEditor.tsx b/ui/src/views/developerKit/SqlEditor.tsx index ff23cba..0ccf8a2 100644 --- a/ui/src/views/developerKit/SqlEditor.tsx +++ b/ui/src/views/developerKit/SqlEditor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, forwardRef, useImperativeHandle } from 'react' +import { useRef, forwardRef, useImperativeHandle } from 'react' import Editor, { Monaco } from '@monaco-editor/react' import { useConfig } from '@/components/ui/ConfigProvider' import type { editor } from 'monaco-editor' @@ -25,7 +25,6 @@ const SqlEditor = forwardRef(( onExecute, onSave, readOnly = false, - height = '100%', }: SqlEditorProps, ref ) => { @@ -66,7 +65,7 @@ const SqlEditor = forwardRef(( // Configure SQL language features monaco.languages.registerCompletionItemProvider('sql', { - provideCompletionItems: (model, position) => { + provideCompletionItems: (model: any, position: any) => { const word = model.getWordUntilPosition(position) const range = { startLineNumber: position.lineNumber, diff --git a/ui/src/views/developerKit/SqlObjectExplorer.tsx b/ui/src/views/developerKit/SqlObjectExplorer.tsx index 8c66767..2a00a41 100644 --- a/ui/src/views/developerKit/SqlObjectExplorer.tsx +++ b/ui/src/views/developerKit/SqlObjectExplorer.tsx @@ -167,7 +167,11 @@ const SqlObjectExplorer = ({ const toggleNode = (nodeId: string) => { setExpandedNodes((prev) => { const next = new Set(prev) - next.has(nodeId) ? next.delete(nodeId) : next.add(nodeId) + if (next.has(nodeId)) { + next.delete(nodeId) + } else { + next.add(nodeId) + } return next }) } diff --git a/ui/src/views/developerKit/SqlQueryManager.tsx b/ui/src/views/developerKit/SqlQueryManager.tsx index 0d491b6..d1f81ab 100644 --- a/ui/src/views/developerKit/SqlQueryManager.tsx +++ b/ui/src/views/developerKit/SqlQueryManager.tsx @@ -17,8 +17,9 @@ import { FaArrowLeft, FaArrowRight, FaEye, + FaCheckCircle, + FaFolderOpen } from 'react-icons/fa' -import { FaCheckCircle } from 'react-icons/fa' import { useLocalization } from '@/utils/hooks/useLocalization' import SqlObjectExplorer, { type SqlExplorerSelectedObject } from './SqlObjectExplorer' import SqlEditor, { SqlEditorRef } from './SqlEditor' @@ -30,7 +31,6 @@ import { useStoreState } from '@/store/store' import { APP_NAME } from '@/constants/app.constant' import { UiEvalService } from '@/services/UiEvalService' import { FcAcceptDatabase } from 'react-icons/fc' -import { FaFolderOpen } from 'react-icons/fa' interface SqlManagerState { dataSources: DataSourceDto[] @@ -124,7 +124,7 @@ const SqlQueryManager = () => { selectedDataSource: items[0].code ?? null, })) } - } catch (error) { + } catch { toast.push( {translate('::App.Platform.FailedtoloadDatasources')} @@ -178,91 +178,6 @@ const SqlQueryManager = () => { const isPostgreSql = selectedDataSourceType === DataSourceTypeEnum.Postgresql const sqlDataDirectoryName = isPostgreSql ? 'PostgresData' : 'SqlData' - const buildTableScriptQuery = (schemaName: string, tableName: string) => { - const fullName = getSafeFullName(schemaName, tableName) - const escapedFullName = escapeSqlLiteral(fullName) - - return `DECLARE @ObjectId INT = OBJECT_ID(N'${escapedFullName}'); - -IF @ObjectId IS NULL -BEGIN - SELECT CAST('' AS NVARCHAR(MAX)) AS Script; - RETURN; -END; - -;WITH cols AS -( - SELECT - c.column_id, - ' ' + QUOTENAME(c.name) + ' ' + - CASE - WHEN t.name IN ('varchar', 'char', 'varbinary', 'binary') THEN - t.name + '(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length AS VARCHAR(10)) END + ')' - WHEN t.name IN ('nvarchar', 'nchar') THEN - t.name + '(' + CASE WHEN c.max_length = -1 THEN 'MAX' ELSE CAST(c.max_length / 2 AS VARCHAR(10)) END + ')' - WHEN t.name IN ('decimal', 'numeric') THEN - t.name + '(' + CAST(c.precision AS VARCHAR(10)) + ',' + CAST(c.scale AS VARCHAR(10)) + ')' - WHEN t.name IN ('datetime2', 'datetimeoffset', 'time') THEN - t.name + '(' + CAST(c.scale AS VARCHAR(10)) + ')' - ELSE t.name - END + - CASE - WHEN ic.object_id IS NOT NULL - THEN ' IDENTITY(' + CAST(ic.seed_value AS VARCHAR(30)) + ',' + CAST(ic.increment_value AS VARCHAR(30)) + ')' - ELSE '' - END + - CASE WHEN c.is_nullable = 1 THEN ' NULL' ELSE ' NOT NULL' END + - ISNULL(' DEFAULT ' + dc.definition, '') AS line - FROM sys.columns c - INNER JOIN sys.types t ON c.user_type_id = t.user_type_id - LEFT JOIN sys.identity_columns ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id - LEFT JOIN sys.default_constraints dc ON c.default_object_id = dc.object_id - WHERE c.object_id = @ObjectId -), -pk AS -( - SELECT - ' CONSTRAINT ' + QUOTENAME(k.name) + ' PRIMARY KEY ' + - CASE WHEN i.type = 1 THEN 'CLUSTERED' ELSE 'NONCLUSTERED' END + - CHAR(13) + CHAR(10) + ' (' + CHAR(13) + CHAR(10) + - ( - SELECT ' ' + QUOTENAME(c.name) + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END + CHAR(13) + CHAR(10) - FROM sys.index_columns ic - INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id - WHERE ic.object_id = i.object_id - AND ic.index_id = i.index_id - AND ic.is_included_column = 0 - ORDER BY ic.key_ordinal - FOR XML PATH(''), TYPE - ).value('.', 'NVARCHAR(MAX)') + - ' )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]' AS line - FROM sys.key_constraints k - INNER JOIN sys.indexes i ON k.parent_object_id = i.object_id AND k.unique_index_id = i.index_id - WHERE k.parent_object_id = @ObjectId - AND k.type = 'PK' -) -SELECT - 'IF OBJECT_ID(N''${escapedFullName}'', ''U'') IS NULL' + CHAR(13) + CHAR(10) + - 'BEGIN' + CHAR(13) + CHAR(10) + - ' CREATE TABLE ${fullName}' + CHAR(13) + CHAR(10) + - ' (' + CHAR(13) + CHAR(10) + - STUFF( - ( - SELECT ',' + CHAR(13) + CHAR(10) + line - FROM cols - ORDER BY column_id - FOR XML PATH(''), TYPE - ).value('.', 'NVARCHAR(MAX)') - ,1,3,'') + - ISNULL( - ( - SELECT ',' + CHAR(13) + CHAR(10) + line - FROM pk - ), - '' - ) + CHAR(13) + CHAR(10) + ' ) ON [PRIMARY]' + CHAR(13) + CHAR(10) + 'END' + CHAR(13) + CHAR(10) + 'GO' AS Script;` - } - const getTableCreateScript = async (schemaName: string, tableName: string): Promise => { if (!state.selectedDataSource) return '' @@ -586,23 +501,6 @@ GO`, })) }, []) - const handleUseTemplateFromDialog = useCallback( - (templateContent: string, templateType: string) => { - // Check if editor has content - const hasUserContent = state.editorContent?.trim() && state.isDirty - - if (hasUserContent) { - // Ask for confirmation - setPendingTemplate({ content: templateContent, type: templateType }) - setShowTemplateConfirmDialog(true) - } else { - // Apply template directly - applyTemplate(templateContent) - } - }, - [state.editorContent, state.isDirty, applyTemplate], - ) - const handleTemplateSelect = useCallback( (template: string, templateType: string) => { // If template is already provided (e.g., from table click), use it diff --git a/ui/src/views/developerKit/SqlResultsGrid.tsx b/ui/src/views/developerKit/SqlResultsGrid.tsx index 6b0f1ab..c55eb6b 100644 --- a/ui/src/views/developerKit/SqlResultsGrid.tsx +++ b/ui/src/views/developerKit/SqlResultsGrid.tsx @@ -178,6 +178,10 @@ const SqlResultsGrid = ({ setValidationError('') } + const normalizeBoolean = (value: unknown): boolean => { + return value === true || value === 'true' || value === 1 || value === '1' + } + const openEditDialog = () => { const row = focusedRow if (!row) return @@ -426,7 +430,7 @@ const SqlResultsGrid = ({ setFormValues((current) => ({ ...current, [column.name]: checked })) } > - {Boolean(value) ? 'True' : 'False'} + {normalizeBoolean(value) ? 'True' : 'False'} ) : isDateColumn(column) ? ( isDateTimeColumn(column) ? ( diff --git a/ui/src/views/developerKit/SqlTableDesignerDialog.tsx b/ui/src/views/developerKit/SqlTableDesignerDialog.tsx index 845f932..d1f4169 100644 --- a/ui/src/views/developerKit/SqlTableDesignerDialog.tsx +++ b/ui/src/views/developerKit/SqlTableDesignerDialog.tsx @@ -1,5 +1,4 @@ -import { useState, useCallback, useMemo, useEffect } from 'react' -import classNames from 'classnames' +import React, { useState, useCallback, useMemo, useEffect } from 'react' import { createPortal } from 'react-dom' import { Button, Dialog, Notification, toast, Checkbox } from '@/components/ui' import { @@ -830,13 +829,6 @@ function generateAlterTableSql( return lines.join('\n') } -const STEPS = [ - 'Sütun Tasarımı', - 'Entity Ayarları', - 'Index / Key', - 'İlişkiler', - 'T-SQL Önizleme', -] as const type Step = 0 | 1 | 2 | 3 | 4 function StepContentWrapper({ children }: { children: React.ReactNode }) { @@ -942,7 +934,11 @@ function SimpleMenuTreeSelect({ const toggle = (code: string) => setExpanded((prev) => { const n = new Set(prev) - n.has(code) ? n.delete(code) : n.add(code) + if (n.has(code)) { + n.delete(code) + } else { + n.add(code) + } return n }) @@ -1485,7 +1481,7 @@ const SqlTableDesignerDialog = ({ const openEditFk = (rel: SqlTableRelation) => { setEditingFkId(rel.id) - const { id: _id, ...rest } = rel + const { ...rest } = rel setFkForm(rest) loadTargetColumns(rest.referencedTable) setFkModalOpen(true) @@ -1548,7 +1544,7 @@ const SqlTableDesignerDialog = ({ const openEditIndex = (idx: TableIndex) => { setEditingIndexId(idx.id) - const { id: _id, ...rest } = idx + const { ...rest } = idx setIndexForm(rest) setIndexModalOpen(true) } @@ -1755,7 +1751,7 @@ const SqlTableDesignerDialog = ({ onClick={importColumnsFromRememberedCreateTable} disabled={isEditMode} > - CREATE TABLE'dan Kolonları Yapıştır + CREATE TABLE Kolonları Yapıştır
@@ -1970,7 +1966,7 @@ const SqlTableDesignerDialog = ({ size="xs" variant="solid" color="green-500" - className="!h-auto !px-2 !py-0.5 hover:!bg-green-600 disabled:opacity-40 disabled:cursor-not-allowed" + className="flex items-center gap-2 !h-auto !px-2 !py-1 hover:!bg-green-600 disabled:opacity-40 disabled:cursor-not-allowed" > {' '} {translate('::ListForms.Wizard.Step1.AddNewMenu') || 'Add Menu'} @@ -1984,7 +1980,7 @@ const SqlTableDesignerDialog = ({ }} size="xs" variant="default" - className="!h-auto !px-2 !py-0.5 border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400" + className="flex items-center gap-2!h-auto !px-2 !py-1 border-gray-300 dark:border-gray-600 text-gray-500 hover:text-red-500 hover:border-red-400" > {' '} {translate('::ListForms.Wizard.ClearSelection') || 'Clear'} @@ -2088,7 +2084,7 @@ const SqlTableDesignerDialog = ({ size="xs" variant="solid" color="indigo-600" - className="!h-auto !px-3 !py-1.5 !rounded-lg hover:!bg-indigo-700 transition-colors" + className="flex items-center gap-2 !h-auto !px-2 !py-1 !rounded-lg hover:!bg-indigo-700 transition-colors" > {' '} {translate('::App.SqlQueryManager.AddRelationship')} @@ -2453,7 +2449,7 @@ const SqlTableDesignerDialog = ({ size="xs" variant="solid" color="indigo-600" - className="!h-auto !px-3 !py-1.5 !rounded-lg hover:!bg-indigo-700 transition-colors" + className="flex items-center gap-2 !h-auto !px-2 !py-1 !rounded-lg hover:!bg-indigo-700 transition-colors" > {translate('::App.SqlQueryManager.AddIndexKey')} diff --git a/ui/src/views/form/FormButtons.tsx b/ui/src/views/form/FormButtons.tsx index 3a79913..bc3e32d 100644 --- a/ui/src/views/form/FormButtons.tsx +++ b/ui/src/views/form/FormButtons.tsx @@ -20,7 +20,6 @@ import { PermissionResults, RowMode } from './types' import { GridDto } from '@/proxy/form/models' import { ROUTES_ENUM } from '@/routes/route.constant' import { usePermission } from '@/utils/hooks/usePermission' -import { GridExtraFilterState } from '../list/Utils' import { usePWA } from '@/utils/hooks/usePWA' import { layoutTypes } from '../admin/listForm/edit/types' @@ -61,9 +60,7 @@ const FormButtons = (props: { onActionView, } = props - const [loading, setLoading] = useState(false) const [deleteRowId, setDeleteRowId] = useState() - const [extraFilters, setExtraFilters] = useState([]) const { checkPermission } = usePermission() const isPwaMode = usePWA() @@ -95,7 +92,6 @@ const FormButtons = (props: { return } - setLoading(true) try { await dataSource.remove(id) setDeleteRowId(undefined) @@ -116,9 +112,7 @@ const FormButtons = (props: { toast.push(, { placement: 'top-end', }) - } finally { - setLoading(false) - } + } } const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton'] diff --git a/ui/src/views/form/FormDevExpress.tsx b/ui/src/views/form/FormDevExpress.tsx index b9d1fe9..d802f79 100644 --- a/ui/src/views/form/FormDevExpress.tsx +++ b/ui/src/views/form/FormDevExpress.tsx @@ -511,7 +511,7 @@ const FormDevExpress = (props: { if (!didAutoFocusRef.current && firstItem?.dataField && !isTouchLikeDevice()) { didAutoFocusRef.current = true const editor = e.component.getEditor(firstItem.dataField) - mode !== 'view' && editor?.focus() + if (mode !== 'view') editor?.focus() } }} > diff --git a/ui/src/views/form/FormNew.tsx b/ui/src/views/form/FormNew.tsx index 3ffac4d..af6d0fc 100644 --- a/ui/src/views/form/FormNew.tsx +++ b/ui/src/views/form/FormNew.tsx @@ -4,7 +4,7 @@ import { DataType } from 'devextreme/common' import { SimpleItem } from 'devextreme/ui/form' import { useEffect, useRef, useState } from 'react' import { Helmet } from 'react-helmet' -import { useNavigate, useParams, useSearchParams } from 'react-router-dom' +import { useParams, useSearchParams } from 'react-router-dom' import FormButtons from './FormButtons' import FormDevExpress from './FormDevExpress' import { FormProps } from './types' @@ -28,11 +28,9 @@ const FormNew = ( const [searchParams] = useSearchParams() const { translate } = useLocalization() const MenuIcon = useCurrentMenuIcon('w-5 h-5') - const navigate = useNavigate() const initializedListFormCodeRef = useRef() const { - fetchData, setFormData, loading, handleSubmit, @@ -126,7 +124,7 @@ const FormNew = ( case 'object': try { newValue = JSON.parse(newParams.get(colFormat.fieldName) as string) - } catch (error) { + } catch { newValue = updatedData[colFormat.fieldName] } break @@ -190,7 +188,7 @@ const FormNew = ( } // Grid'den gelen columnFormat'ları kullanarak default değerleri set et - for (const colFormat of gridDto?.columnFormats) { + for (const colFormat of gridDto?.columnFormats || []) { if (!colFormat.fieldName) { continue } diff --git a/ui/src/views/form/SubForms.tsx b/ui/src/views/form/SubForms.tsx index 8cb78e7..6c15c7c 100644 --- a/ui/src/views/form/SubForms.tsx +++ b/ui/src/views/form/SubForms.tsx @@ -87,7 +87,7 @@ const SubForms = (props: { className="mt-2" > - {subForms.map((subForm, i) => { + {subForms.map((subForm) => { return ( - {subForms.map((subForm, i) => { + {subForms.map((subForm) => { return ( {subForm.tabType == SubFormTabTypeEnum.List && ( @@ -213,7 +213,7 @@ const SubForms = (props: { ))} {subForm.tabType == SubFormTabTypeEnum.Chart && ( = ({ const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ y: 0, startTop: 0 }) const buttonRef = useRef(null) - const [showEntityInfo, setShowEntityInfo] = useState(false) const { translate } = useLocalization() // Fetch activities @@ -48,7 +47,7 @@ export const NotePanel: React.FC = ({ const handleDownloadFile = async (fileData: any) => { if (!fileData?.SavedFileName) return try { - await noteService.downloadFile(fileData.SavedFileName, fileData.FileName, fileData.FileType) + await noteService.downloadFile(fileData.SavedFileName, fileData.FileName) } catch (err) { console.error(translate('::ListForms.ListForm.NotesPanel.DownloadFailed'), err) } @@ -177,9 +176,7 @@ export const NotePanel: React.FC = ({ {/* 👇 Açılır Kayıt Bilgisi İçeriği */}
diff --git a/ui/src/views/form/useFormData.tsx b/ui/src/views/form/useFormData.tsx index c602389..67d2686 100644 --- a/ui/src/views/form/useFormData.tsx +++ b/ui/src/views/form/useFormData.tsx @@ -236,9 +236,7 @@ const useGridData = (props: { let parsedEditorOptions: Record = {} const colData = gridDto.columnFormats.find((x) => x.fieldName === i.dataField) - try { - parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {} - } catch {} + parsedEditorOptions = i.editorOptions ? JSON.parse(i.editorOptions) : {} const lookupEditorOptions = colData?.lookupDto?.dataSourceType ? { dataSource: getLookupDataSource(colData?.editorOptions, colData, formData), diff --git a/ui/src/views/form/useLookupDataSource.ts b/ui/src/views/form/useLookupDataSource.ts index bf83777..6fcca5d 100644 --- a/ui/src/views/form/useLookupDataSource.ts +++ b/ui/src/views/form/useLookupDataSource.ts @@ -111,7 +111,7 @@ const createLookupApiDataSource = ( let { data } = response if (!data) return [] if (!Array.isArray(data)) data = [data] - return data.map((a: any) => ({ + return data.map(() => ({ key: eval(keySelector), name: eval(nameSelector), group: eval(groupSelector), diff --git a/ui/src/views/forum/Forum.tsx b/ui/src/views/forum/Forum.tsx index dc71295..ca01239 100644 --- a/ui/src/views/forum/Forum.tsx +++ b/ui/src/views/forum/Forum.tsx @@ -1,6 +1,5 @@ -import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' -import { useStoreState } from '@/store/store' -import React, { useState, useEffect } from 'react' +import { ForumCategory, ForumTopic } from '@/proxy/forum/forum' +import { useState, useEffect } from 'react' import { useForumData } from './useForumData' import { ForumView } from './forum/ForumView' import { Container } from '@/components/shared' @@ -12,7 +11,6 @@ import { Button } from '@/components/ui' export function Forum() { const { translate } = useLocalization() - const { user } = useStoreState((state) => state.auth) const { categories, topics, @@ -80,8 +78,6 @@ export function Forum() { onCreatePost={(postData) => createPost(postData).then(() => {})} onLikePost={(id) => likePost(id).then(() => {})} onUnlikePost={(id) => unlikePost(id).then(() => {})} - currentUserId={user.id} - currentUserName={user.name} selectedCategory={selectedCategory} selectedTopic={selectedTopic} viewState={forumViewState} diff --git a/ui/src/views/forum/admin/AdminView.tsx b/ui/src/views/forum/admin/AdminView.tsx index bbb8abd..209af28 100644 --- a/ui/src/views/forum/admin/AdminView.tsx +++ b/ui/src/views/forum/admin/AdminView.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { FaFolder, FaCommentAlt, FaFileAlt, FaChartBar } from 'react-icons/fa' import { CategoryManagement } from './CategoryManagement' import { TopicManagement } from './TopicManagement' diff --git a/ui/src/views/forum/admin/CategoryManagement.tsx b/ui/src/views/forum/admin/CategoryManagement.tsx index ff88cf1..e27e3f7 100644 --- a/ui/src/views/forum/admin/CategoryManagement.tsx +++ b/ui/src/views/forum/admin/CategoryManagement.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { FaPlus, FaEdit, @@ -50,17 +50,6 @@ export function CategoryManagement({ const { tenant } = useStoreState((state) => state.auth) const [showCreateForm, setShowCreateForm] = useState(false) const [editingCategory, setEditingCategory] = useState(null) - const [formData, setFormData] = useState({ - name: '', - slug: '', - description: '', - icon: '', - displayOrder: 0, - isActive: true, - isLocked: false, - tenantId: '', - }) - const [submitting, setSubmitting] = useState(false) const [showConfirm, setShowConfirm] = useState(false) const [categoryToDelete, setCategoryToDelete] = useState(null) @@ -80,52 +69,8 @@ export function CategoryManagement({ setShowConfirm(true) } - const resetForm = () => { - setFormData({ - name: '', - slug: '', - description: '', - icon: '', - displayOrder: 0, - isActive: true, - isLocked: false, - tenantId: '', - }) - setShowCreateForm(false) - setEditingCategory(null) - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (submitting) return - - try { - setSubmitting(true) - if (editingCategory) { - await onUpdateCategory(editingCategory.id, formData) - } else { - await onCreateCategory(formData) - } - resetForm() - } catch (error) { - console.error('Error submitting form:', error) - } finally { - setSubmitting(false) - } - } - const handleEdit = (category: ForumCategory) => { setEditingCategory(category) - setFormData({ - name: category.name, - slug: category.slug, - description: category.description, - icon: category.icon, - displayOrder: category.displayOrder, - isActive: category.isActive, - isLocked: category.isLocked, - tenantId: tenant.tenantId ?? '', - }) setShowCreateForm(true) } @@ -145,20 +90,6 @@ export function CategoryManagement({ } } - const handleDelete = async (id: string) => { - if ( - confirm( - 'Are you sure you want to delete this category? This will also delete all topics and posts in this category.', - ) - ) { - try { - await onDeleteCategory(id) - } catch (error) { - console.error('Error deleting category:', error) - } - } - } - return (
{/* Create/Edit Form */} @@ -200,7 +131,7 @@ export function CategoryManagement({ } }} > - {({ errors, touched, isSubmitting, values }) => ( + {({ errors, touched, isSubmitting }) => (
@@ -332,7 +263,7 @@ export function CategoryManagement({
) : (
- {categories + {[...categories] .sort((a, b) => a.displayOrder - b.displayOrder) .map((category) => (
state.auth) - const [content, setContent] = useState('') const [showCreateForm, setShowCreateForm] = useState(false) const [editingPost, setEditingPost] = useState(null) - const [formData, setFormData] = useState({ - topicId: '', - parentPostId: '', - tenantId: '', - isAcceptedAnswer: false, - }) - const [submitting, setSubmitting] = useState(false) const [showConfirm, setShowConfirm] = useState(false) const [postToDelete, setPostToDelete] = useState(null) @@ -72,48 +64,8 @@ export function PostManagement({ setShowConfirm(true) } - const resetForm = () => { - setFormData({ - topicId: '', - parentPostId: '', - tenantId: '', - isAcceptedAnswer: false, - }) - setShowCreateForm(false) - setEditingPost(null) - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (submitting || !content.trim()) return - - try { - setSubmitting(true) - const data = { ...formData, content: content.trim() } - - if (editingPost) { - await onUpdatePost(editingPost.id, data) - } else { - await onCreatePost(data) - } - - resetForm() - } catch (error) { - console.error('Error submitting form:', error) - } finally { - setSubmitting(false) - } - } - const handleEdit = (post: ForumPost) => { setEditingPost(post) - setFormData({ - topicId: post.topicId, - parentPostId: '', - tenantId: tenant.tenantId ?? '', - isAcceptedAnswer: post.isAcceptedAnswer, - }) - setContent(post.content) setShowCreateForm(true) } @@ -129,25 +81,11 @@ export function PostManagement({ } } - const handleDelete = async (id: string) => { - if (confirm('Are you sure you want to delete this post?')) { - try { - await onDeletePost(id) - } catch (error) { - console.error('Error deleting post:', error) - } - } - } - const getTopicTitle = (topicId: string) => { const topic = topics.find((t) => t.id === topicId) return topic ? topic.title : 'Unknown Topic' } - const formatDate = (value: string | Date) => { - return showDbDateAsIs(typeof value === 'string' ? value : value.toISOString()) - } - const postValidationSchema = Yup.object().shape({ topicId: Yup.string().required(), content: Yup.string() @@ -352,7 +290,7 @@ export function PostManagement({
) : (
- {posts + {[...posts] .sort( (a, b) => new Date(b.creationTime).getTime() - new Date(a.creationTime).getTime(), ) @@ -383,7 +321,7 @@ export function PostManagement({
- {formatDate(post.creationTime)} + {formatForumDate(post.creationTime)}
{post.likeCount} likes diff --git a/ui/src/views/forum/admin/TopicManagement.tsx b/ui/src/views/forum/admin/TopicManagement.tsx index 7e02797..cc75e76 100644 --- a/ui/src/views/forum/admin/TopicManagement.tsx +++ b/ui/src/views/forum/admin/TopicManagement.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { FaPlus, FaEdit, @@ -19,7 +19,7 @@ import { Formik, Form, Field } from 'formik' import * as Yup from 'yup' import { FormContainer, FormItem, Button } from '@/components/ui' import { ConfirmDialog } from '@/components/shared' -import { showDbDateAsIs } from '@/utils/dateUtils' +import { formatForumDate } from '../forum/utils' interface TopicManagementProps { topics: ForumTopic[] @@ -61,67 +61,16 @@ export function TopicManagement({ const { tenant } = useStoreState((state) => state.auth) const [showCreateForm, setShowCreateForm] = useState(false) const [editingTopic, setEditingTopic] = useState(null) - const [formData, setFormData] = useState({ - title: '', - content: '', - categoryId: '', - isPinned: false, - isLocked: false, - isSolved: false, - tenantId: '', - }) - const [submitting, setSubmitting] = useState(false) const [showConfirm, setShowConfirm] = useState(false) const [topicToDelete, setTopicToDelete] = useState(null) - const resetForm = () => { - setFormData({ - title: '', - content: '', - categoryId: '', - isPinned: false, - isLocked: false, - isSolved: false, - tenantId: '', - }) - setShowCreateForm(false) - setEditingTopic(null) - } const confirmDeleteTopic = (topic: ForumTopic) => { setTopicToDelete(topic) setShowConfirm(true) } - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (submitting) return - - try { - setSubmitting(true) - if (editingTopic) { - await onUpdateTopic(editingTopic.id, formData) - } else { - await onCreateTopic(formData) - } - resetForm() - } catch (error) { - console.error('Error submitting form:', error) - } finally { - setSubmitting(false) - } - } - const handleEdit = (topic: ForumTopic) => { setEditingTopic(topic) - setFormData({ - title: topic.title, - content: topic.content, - categoryId: topic.categoryId, - isPinned: topic.isPinned, - isLocked: topic.isLocked, - isSolved: topic.isSolved, - tenantId: tenant.tenantId ?? '', - }) setShowCreateForm(true) } @@ -161,29 +110,11 @@ export function TopicManagement({ } } - const handleDelete = async (id: string) => { - if ( - confirm( - 'Are you sure you want to delete this topic? This will also delete all posts in this topic.', - ) - ) { - try { - await onDeleteTopic(id) - } catch (error) { - console.error('Error deleting topic:', error) - } - } - } - const getCategoryName = (categoryId: string) => { const category = categories.find((c) => c.id === categoryId) return category ? category.name : 'Unknown Category' } - const formatDate = (value: string | Date) => { - return showDbDateAsIs(typeof value === 'string' ? value : value.toISOString()) - } - const topicInitialValues = { title: '', content: '', @@ -357,7 +288,7 @@ export function TopicManagement({
) : (
- {topics + {[...topics] .sort( (a, b) => new Date(b.creationTime).getTime() - new Date(a.creationTime).getTime(), ) @@ -391,7 +322,7 @@ export function TopicManagement({
{getCategoryName(topic.categoryId)} by {topic.authorName} - {formatDate(topic.creationTime)} + {formatForumDate(topic.creationTime)}
diff --git a/ui/src/views/forum/forum/ForumCategoryCard.tsx b/ui/src/views/forum/forum/ForumCategoryCard.tsx index 8bc1ec5..174f8cd 100644 --- a/ui/src/views/forum/forum/ForumCategoryCard.tsx +++ b/ui/src/views/forum/forum/ForumCategoryCard.tsx @@ -1,5 +1,6 @@ -import { FaComment, FaLock, FaArrowUp } from 'react-icons/fa'; +import { FaComment, FaLock, FaArrowUp } from 'react-icons/fa' import { ForumCategory } from '@/proxy/forum/forum' +import { formatForumRelativeDate } from './utils' interface CategoryCardProps { category: ForumCategory @@ -7,20 +8,6 @@ interface CategoryCardProps { } export function ForumCategoryCard({ category, onClick }: CategoryCardProps) { - const formatDate = (dateString?: string) => { - if (!dateString) return 'Never' - - const date = new Date(dateString) - const now = new Date() - - // Zaman farkını doğru hesapla - const diffTime = date.getTime() - now.getTime() - const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24)) - - // Intl.RelativeTimeFormat doğru yönle çalışır - return new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(diffDays, 'day') - } - return (
Last post
-
{formatDate(category.lastPostDate)}
+
+ {formatForumRelativeDate(category.lastPostDate)} +
diff --git a/ui/src/views/forum/forum/ForumPostCard.tsx b/ui/src/views/forum/forum/ForumPostCard.tsx index 42b106e..0a9b068 100644 --- a/ui/src/views/forum/forum/ForumPostCard.tsx +++ b/ui/src/views/forum/forum/ForumPostCard.tsx @@ -1,10 +1,9 @@ -import React from 'react' import { FaHeart, FaCheckCircle, FaReply } from 'react-icons/fa' import { ForumPost } from '@/proxy/forum/forum' import { AVATAR_URL } from '@/constants/app.constant' import { useLocalization } from '@/utils/hooks/useLocalization' -import { showDbDateAsIs } from '@/utils/dateUtils' import { Button } from '@/components/ui' +import { formatForumDate } from './utils' interface PostCardProps { post: ForumPost @@ -22,9 +21,6 @@ export function ForumPostCard({ isLiked = false, }: PostCardProps) { const { translate } = useLocalization() - const formatDate = (value: string | Date) => { - return showDbDateAsIs(typeof value === 'string' ? value : value.toISOString()) - } return (
)}
- {formatDate(post.creationTime)} + + {formatForumDate(post.creationTime)} +
diff --git a/ui/src/views/forum/forum/ForumTopicCard.tsx b/ui/src/views/forum/forum/ForumTopicCard.tsx index e85d0c9..eeec9c3 100644 --- a/ui/src/views/forum/forum/ForumTopicCard.tsx +++ b/ui/src/views/forum/forum/ForumTopicCard.tsx @@ -1,7 +1,8 @@ -import { FaComment, FaHeart, FaEye, FaThumbtack, FaLock, FaCheckCircle } from 'react-icons/fa'; +import { FaComment, FaHeart, FaEye, FaThumbtack, FaLock, FaCheckCircle } from 'react-icons/fa' import { ForumTopic } from '@/proxy/forum/forum' import { AVATAR_URL } from '@/constants/app.constant' import { useLocalization } from '@/utils/hooks/useLocalization' +import { formatForumRelativeDate } from './utils' interface TopicCardProps { topic: ForumTopic @@ -10,19 +11,6 @@ interface TopicCardProps { export function ForumTopicCard({ topic, onClick }: TopicCardProps) { const { translate } = useLocalization() - const formatDate = (dateString?: string) => { - if (!dateString) return 'Never' - - const date = new Date(dateString) - const now = new Date() - - // Zaman farkını doğru hesapla - const diffTime = date.getTime() - now.getTime() - const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24)) - - // Intl.RelativeTimeFormat doğru yönle çalışır - return new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(diffDays, 'day') - } return (
{topic.authorName}
-
{formatDate(topic.creationTime)}
+
+ {formatForumRelativeDate(topic.creationTime)} +
@@ -82,7 +72,7 @@ export function ForumTopicCard({ topic, onClick }: TopicCardProps) { {translate('::App.Forum.TopicManagement.Lastreplyby')}{' '} {topic.lastPostUserName} {' '} - {formatDate(topic.lastPostDate)} + {formatForumRelativeDate(topic.lastPostDate)}
diff --git a/ui/src/views/forum/forum/ForumView.tsx b/ui/src/views/forum/forum/ForumView.tsx index c74721c..7243dc2 100644 --- a/ui/src/views/forum/forum/ForumView.tsx +++ b/ui/src/views/forum/forum/ForumView.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { FaArrowLeft, FaPlus, FaSpinner, FaSearch } from 'react-icons/fa' import { CreateTopicModal } from './CreateTopicModal' import { CreatePostModal } from './CreatePostModal' @@ -34,8 +34,6 @@ interface ForumViewProps { }) => Promise onLikePost: (id: string) => Promise onUnlikePost: (id: string) => Promise - currentUserId: string - currentUserName: string selectedCategory?: ForumCategory | null selectedTopic?: ForumTopic | null viewState?: 'categories' | 'topics' | 'posts' @@ -161,28 +159,6 @@ export function ForumView({ } } - const handleBack = () => { - if (viewState === 'posts') { - onTopicSelect?.(null) // 🔧 seçili topic'i temizle - if (onViewStateChange) { - onViewStateChange('topics') - } else { - setLocalViewState('topics') - setLocalSelectedTopic(null) // 🔧 local state için de temizle - } - } else if (viewState === 'topics') { - onCategorySelect?.(null) // 🔧 seçili category'yi temizle - onTopicSelect?.(null) // 🔧 topic'i de temizlik amaçlı sıfırla - if (onViewStateChange) { - onViewStateChange('categories') - } else { - setLocalViewState('categories') - setLocalSelectedCategory(null) - setLocalSelectedTopic(null) - } - } - } - const handleBreadcrumbClick = (target: 'forum' | 'category') => { if (target === 'forum') { onViewStateChange?.('categories') @@ -260,12 +236,19 @@ export function ForumView({ const isLiked = likedPosts.has(postId) if (isLiked) { - isFirst ? await forumService.unlikeTopic(postId) : await onUnlikePost(postId) + if (isFirst) { + await forumService.unlikeTopic(postId) + } else { + await onUnlikePost(postId) + } } else { - isFirst ? await forumService.likeTopic(postId) : await onLikePost(postId) + if (isFirst) { + await forumService.likeTopic(postId) + } else { + await onLikePost(postId) + } } - // 🔁 toggle liked state setLikedPosts((prev) => { const updated = new Set(prev) if (isLiked) updated.delete(postId) @@ -401,7 +384,7 @@ export function ForumView({
- {categories + {[...categories] .filter((cat) => cat.isActive) .sort((a, b) => a.displayOrder - b.displayOrder) .map((category) => ( @@ -422,7 +405,7 @@ export function ForumView({

{selectedCategory.name}

- {filteredTopics + {[...filteredTopics] .sort((a, b) => { if (a.isPinned && !b.isPinned) return -1 if (!a.isPinned && b.isPinned) return 1 diff --git a/ui/src/views/forum/forum/SearchModal.tsx b/ui/src/views/forum/forum/SearchModal.tsx index 26e5fb5..d3e657c 100644 --- a/ui/src/views/forum/forum/SearchModal.tsx +++ b/ui/src/views/forum/forum/SearchModal.tsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect } from 'react' +import { useState, useEffect, type KeyboardEvent } from 'react' import { FaTimes, FaSearch, FaFolder, FaRegComment, FaFileAlt } from 'react-icons/fa' import { ForumCategory, ForumPost, ForumTopic } from '@/proxy/forum/forum' -import { showDbDateAsIs } from '@/utils/dateUtils' import { useForumSearch } from '@/utils/hooks/useForumSearch' import Button from '@/components/ui/Button' +import { formatForumDate } from './utils' interface SearchModalProps { isOpen: boolean @@ -26,7 +26,7 @@ export function SearchModal({ onTopicSelect, onPostSelect, }: SearchModalProps) { - const { searchQuery, setSearchQuery, searchResults, clearSearch, hasResults } = useForumSearch({ + const { searchQuery, setSearchQuery, searchResults, hasResults } = useForumSearch({ categories, topics, posts, @@ -40,10 +40,15 @@ export function SearchModal({ } }, [isOpen, searchResults]) - const handleKeyDown = (e: React.KeyboardEvent) => { + const handleKeyDown = (e: KeyboardEvent) => { const totalResults = searchResults.categories.length + searchResults.topics.length + searchResults.posts.length + if (totalResults === 0 && ['ArrowDown', 'ArrowUp', 'Enter'].includes(e.key)) { + e.preventDefault() + return + } + if (e.key === 'ArrowDown') { e.preventDefault() setSelectedIndex((prev) => (prev + 1) % totalResults) @@ -85,10 +90,6 @@ export function SearchModal({ } } - const formatDate = (value: string | Date) => { - return showDbDateAsIs(typeof value === 'string' ? value : value.toISOString()) - } - const getTopicTitle = (topicId: string) => { const topic = topics.find((t) => t.id === topicId) return topic ? topic.title : 'Unknown Topic' @@ -127,7 +128,7 @@ export function SearchModal({
) : !hasResults ? (
-

No results found for "{searchQuery}"

+

No results found for {searchQuery}

) : (
@@ -209,7 +210,7 @@ export function SearchModal({ {topic.title}
- by {topic.authorName} • {formatDate(topic.creationTime)} + by {topic.authorName} • {formatForumDate(topic.creationTime)}
@@ -261,7 +262,7 @@ export function SearchModal({ dangerouslySetInnerHTML={{ __html: post.content }} >
- by {post.authorName} • {formatDate(post.creationTime)} + by {post.authorName} • {formatForumDate(post.creationTime)}
diff --git a/ui/src/views/forum/forum/utils.tsx b/ui/src/views/forum/forum/utils.tsx index 054822d..5460333 100644 --- a/ui/src/views/forum/forum/utils.tsx +++ b/ui/src/views/forum/forum/utils.tsx @@ -1,16 +1,15 @@ import { ForumPost } from '@/proxy/forum/forum' +import { showDbDateAsIs } from '@/utils/dateUtils' export function buildPostTree(posts: ForumPost[]): ForumPost[] { const postMap = new Map() - // 1. Her post için children array'i eklenmiş yeni bir nesne oluştur posts.forEach((post) => { postMap.set(post.id, { ...post, children: [] }) }) const roots: ForumPost[] = [] - // 2. Her post'un parent'ı varsa ilgili parent'ın children listesine ekle postMap.forEach((post) => { if (post.parentPostId && postMap.has(post.parentPostId)) { postMap.get(post.parentPostId)!.children.push(post) @@ -21,3 +20,16 @@ export function buildPostTree(posts: ForumPost[]): ForumPost[] { return roots } + +export function formatForumDate(value: string | Date) { + return showDbDateAsIs(typeof value === 'string' ? value : value.toISOString()) +} + +export function formatForumRelativeDate(dateString?: string) { + if (!dateString) return 'Never' + + const diffTime = new Date(dateString).getTime() - Date.now() + const diffDays = Math.round(diffTime / (1000 * 60 * 60 * 24)) + + return new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(diffDays, 'day') +} diff --git a/ui/src/views/intranet/widgets/Surveys.tsx b/ui/src/views/intranet/widgets/Surveys.tsx index e2aea6e..94acc6d 100644 --- a/ui/src/views/intranet/widgets/Surveys.tsx +++ b/ui/src/views/intranet/widgets/Surveys.tsx @@ -30,7 +30,7 @@ const Surveys: React.FC = ({ surveys, onTakeSurvey }) => {
- {surveys?.map((survey, index) => { + {surveys?.map((survey) => { const daysLeft = dayjs(survey.deadline).diff(dayjs(), 'day') const urgency = daysLeft <= 3 ? 'urgent' : daysLeft <= 7 ? 'warning' : 'normal' const isCompleted = !!survey.myResponse diff --git a/ui/src/views/list/Chart.tsx b/ui/src/views/list/Chart.tsx index 475e60a..424a6cc 100644 --- a/ui/src/views/list/Chart.tsx +++ b/ui/src/views/list/Chart.tsx @@ -46,7 +46,7 @@ type LookupDisplayValues = Record> const Chart = (props: ChartProps) => { // State UserId güncellemesi için const { userName } = useStoreState((s) => s.auth.user) - const { id, listFormCode, filter, isSubForm, level, gridDto, refreshGridDto } = props + const { id, listFormCode, isSubForm, gridDto, refreshGridDto } = props const { translate } = useLocalization() const { checkPermission } = usePermission() const isPwaMode = usePWA() @@ -102,8 +102,6 @@ const Chart = (props: ChartProps) => { searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(), ) - const [allSeries, setAllSeries] = useState([]) - const lookupColumns = useMemo( () => gridDto?.columnFormats @@ -137,8 +135,6 @@ const Chart = (props: ChartProps) => { const initialSeries = gridDto.gridOptions.seriesDto.map((s, index) => ({ ...s, index })) const userSeriesData = initialSeries.filter((s) => s.userId === userName) - setAllSeries(initialSeries) - // Kullanıcının serisi varsa onu kullan, yoksa tüm serileri kullan const seriesToUse = userSeriesData.length > 0 ? userSeriesData : initialSeries diff --git a/ui/src/views/list/ChartDrawer.tsx b/ui/src/views/list/ChartDrawer.tsx index 863cafe..93f6513 100644 --- a/ui/src/views/list/ChartDrawer.tsx +++ b/ui/src/views/list/ChartDrawer.tsx @@ -22,6 +22,22 @@ interface ChartDrawerProps { onPreviewChange: (series: ChartSeriesDto[]) => void } +interface PreviewUpdaterProps { + series: ChartSeriesDto[] + onPreviewChange: (series: ChartSeriesDto[]) => void +} + +const PreviewUpdater = ({ series, onPreviewChange }: PreviewUpdaterProps) => { + useEffect(() => { + const validSeries = series.filter((s) => s.argumentField && s.valueField && s.summaryType) + if (validSeries.length > 0) { + onPreviewChange(validSeries) + } + }, [onPreviewChange, series]) + + return null +} + const ChartDrawer = ({ open, onClose, @@ -126,18 +142,10 @@ const ChartDrawer = ({ }} > {({ setFieldValue, values, isSubmitting }) => { - // Preview'ı her değişiklikte otomatik güncelle - useEffect(() => { - const validSeries = values.series.filter( - (s) => s.argumentField && s.valueField && s.summaryType, - ) - if (validSeries.length > 0) { - onPreviewChange(validSeries) - } - }, [values.series]) - return ( + + {/* Header */}

diff --git a/ui/src/views/list/GanttView.tsx b/ui/src/views/list/GanttView.tsx index 60fc791..56bbb4d 100644 --- a/ui/src/views/list/GanttView.tsx +++ b/ui/src/views/list/GanttView.tsx @@ -1,6 +1,6 @@ import Container from '@/components/shared/Container' import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant' -import { GridDto, SubFormTabTypeEnum } from '@/proxy/form/models' +import { GridDto } from '@/proxy/form/models' import { useLocalization } from '@/utils/hooks/useLocalization' import Gantt, { Column, @@ -30,7 +30,6 @@ import CustomStore from 'devextreme/data/custom_store' import { GridColumnData } from './GridColumnData' import { Loading } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' -import SubForms from '../form/SubForms' interface GanttViewProps { listFormCode: string @@ -42,7 +41,7 @@ interface GanttViewProps { } const GanttView = (props: GanttViewProps) => { - const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props + const { listFormCode, searchParams, isSubForm, gridDto: extGridDto } = props const { translate } = useLocalization() const isPwaMode = usePWA() diff --git a/ui/src/views/list/List.tsx b/ui/src/views/list/List.tsx index c915997..a5e875f 100644 --- a/ui/src/views/list/List.tsx +++ b/ui/src/views/list/List.tsx @@ -279,7 +279,7 @@ const List: React.FC = () => { {viewMode === 'chart' && ( > const Pivot = (props: PivotProps) => { - const { listFormCode, searchParams, isSubForm, level, gridDto } = props + const { listFormCode, searchParams, isSubForm, gridDto } = props const { translate } = useLocalization() const { checkPermission } = usePermission() const isPwaMode = usePWA() @@ -78,11 +77,6 @@ const Pivot = (props: PivotProps) => { return gridDto?.gridOptions.stateStoringDto?.storageKey ?? '' }, [gridDto?.gridOptions.stateStoringDto?.storageKey]) - const { filterToolbarData, ...filterData } = useFilters({ - gridDto, - gridRef, - listFormCode, - }) const { createSelectDataSource } = useListFormCustomDataSource({ gridRef }) const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({ @@ -147,25 +141,6 @@ const Pivot = (props: PivotProps) => { } }, []) - const moveAllFieldsToFilterArea = useCallback(() => { - const grid = gridRef.current?.instance() - if (!grid) return - - const ds = grid.getDataSource() - if (!ds) return - - const fields = ds.fields() - - fields.forEach((field: any) => { - field.area = 'filter' // tüm alanları filtre alanına taşı - field.areaIndex = undefined - }) - - ds.fields(fields) - ds.reload() // PivotGrid’i yeniden yükle - grid.repaint() // UI güncelle - }, []) - const resetPivotGridState = useCallback(async () => { const grid = gridRef.current?.instance() if (grid) { diff --git a/ui/src/views/list/SchedulerView.tsx b/ui/src/views/list/SchedulerView.tsx index 36f6ba4..f20de1a 100644 --- a/ui/src/views/list/SchedulerView.tsx +++ b/ui/src/views/list/SchedulerView.tsx @@ -5,6 +5,7 @@ import { GridDto, PlatformEditorTypes, UiLookupDataSourceTypeEnum, + EditingFormItemDto } from '@/proxy/form/models' import { useLocalization } from '@/utils/hooks/useLocalization' import Scheduler, { @@ -27,7 +28,6 @@ import { usePWA } from '@/utils/hooks/usePWA' import CustomStore from 'devextreme/data/custom_store' import { Loading } from '@/components/shared' import { usePermission } from '@/utils/hooks/usePermission' -import { EditingFormItemDto } from '@/proxy/form/models' import { RowMode, SimpleItemWithColData } from '../form/types' import { captionize } from 'devextreme/core/utils/inflector' import { AppointmentFormOpeningEvent } from 'devextreme/ui/scheduler' diff --git a/ui/src/views/list/Tree.tsx b/ui/src/views/list/Tree.tsx index ae5f41f..f9df011 100644 --- a/ui/src/views/list/Tree.tsx +++ b/ui/src/views/list/Tree.tsx @@ -582,7 +582,7 @@ const Tree = (props: TreeProps) => { useMobileEditPopup || (gridDto?.gridOptions.editingOptionDto?.popup?.fullScreen ?? false), ) - for (const colFormat of gridDto?.columnFormats) { + for (const colFormat of gridDto?.columnFormats ?? []) { if (!colFormat.fieldName) { continue } @@ -631,7 +631,8 @@ const Tree = (props: TreeProps) => { case 'object': try { e.data[colFormat.fieldName] = JSON.parse(val) - } catch {} + } + catch {} break default: e.data[colFormat.fieldName] = val diff --git a/ui/src/views/list/editors/ImageUploadEditorComponent.tsx b/ui/src/views/list/editors/ImageUploadEditorComponent.tsx index 331cb79..838ee92 100644 --- a/ui/src/views/list/editors/ImageUploadEditorComponent.tsx +++ b/ui/src/views/list/editors/ImageUploadEditorComponent.tsx @@ -1,4 +1,5 @@ -import { ReactElement, useRef, useState } from 'react' +import type { ReactElement } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { FaSpinner, FaUpload } from 'react-icons/fa' import { Button } from '@/components/ui' import { @@ -7,43 +8,105 @@ import { showImageHoverPreview, } from '../../form/editors/imageHoverPreview' +type ImageUploadOptions = { + accept?: string + fileFieldName?: string + height?: number | string + multiple?: boolean | string + uploadUrl?: string + width?: number | string +} + +const parseJsonObject = (value: unknown) => { + if (!value) return undefined + if (typeof value === 'object' && !Array.isArray(value)) return value as Record + if (typeof value !== 'string') return undefined + + try { + const parsed = JSON.parse(value) + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) + ? (parsed as Record) + : undefined + } catch { + return undefined + } +} + +const normalizeImageValue = (value: unknown): string[] => { + if (!value) return [] + if (Array.isArray(value)) return value.flatMap(normalizeImageValue) + + if (typeof value === 'string') { + const trimmed = value.trim() + if (!trimmed) return [] + + if (trimmed.startsWith('[')) { + try { + const parsed = JSON.parse(trimmed) + if (Array.isArray(parsed)) return parsed.flatMap(normalizeImageValue) + } catch { + return [trimmed] + } + } + + return [trimmed] + } + + return [String(value)].filter(Boolean) +} + +const normalizeSize = (value: unknown, fallback: number) => { + const size = Number(value) + return Number.isFinite(size) && size > 0 ? size : fallback +} + +const resolveOptions = (column: any): ImageUploadOptions => { + const configuredOptions = column?.extras?.imageUploadOptions + if (configuredOptions && Object.keys(configuredOptions).length > 0) { + return configuredOptions + } + + const parsedOptions = parseJsonObject(column?.extras?.editorOptions ?? column?.editorOptions) + return parsedOptions ?? {} +} + +const resolveUploadedUrl = (data: unknown) => { + if (typeof data === 'string') return data + if (!data || typeof data !== 'object') return '' + + const record = data as Record + return String(record.url ?? record.fileUrl ?? record.path ?? '').trim() +} + +const toBase64 = (file: File): Promise => + new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(reader.result as string) + reader.onerror = reject + reader.readAsDataURL(file) + }) + const ImageUploadEditorComponent = (cellElement: any): ReactElement => { const col = cellElement.column + const [uploading, setUploading] = useState(false) + const inputRef = useRef(null) + const [urls, setUrls] = useState(() => normalizeImageValue(cellElement.value)) + + useEffect(() => { + setUrls(normalizeImageValue(cellElement.value)) + }, [cellElement.value]) + + const options = useMemo(() => resolveOptions(col), [col]) + if (!col) { return <> } - // options: önce column.extras.imageUploadOptions, yoksa editorOptions JSON'undan parse et - let options: any = col.extras?.imageUploadOptions - if (!options || Object.keys(options).length === 0) { - try { - const raw = col.extras?.editorOptions ?? col.editorOptions - if (raw) { - const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw - options = parsed - } - } catch { - // ignore - } - } - options = options ?? {} // JSON'dan string olarak gelebilir: "true" / "false" const isMultiple: boolean = options.multiple === true || options.multiple === 'true' - const [uploading, setUploading] = useState(false) - const inputRef = useRef(null) - - const thumbW: number = options.width ?? 40 - const thumbH: number = options.height ?? 40 - - const currentValue = cellElement.value - const initialUrls: string[] = currentValue - ? Array.isArray(currentValue) - ? currentValue.filter(Boolean) - : [currentValue].filter(Boolean) - : [] - - const [urls, setUrls] = useState(initialUrls) + const thumbW = normalizeSize(options.width, 40) + const thumbH = normalizeSize(options.height, 40) const removeImage = (index: number) => { const newUrls = urls.filter((_, i) => i !== index) @@ -55,14 +118,6 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { } } - const toBase64 = (file: File): Promise => - new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onload = () => resolve(reader.result as string) - reader.onerror = reject - reader.readAsDataURL(file) - }) - const handleFiles = async (files: FileList | null) => { if (!files?.length) return @@ -79,7 +134,7 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { const res = await fetch(options.uploadUrl, { method: 'POST', body: formData }) if (!res.ok) throw new Error(`Upload failed: ${res.status}`) const data = await res.json() - const url: string = data?.url ?? data?.fileUrl ?? data?.path ?? data + const url = resolveUploadedUrl(data) uploadedUrls.push(url) } else { // uploadUrl yapılandırılmamışsa base64 olarak sakla @@ -107,10 +162,6 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { showImageHoverPreview(url, event)} - onMouseMove={(event) => showImageHoverPreview(url, event)} - onMouseLeave={hideImageHoverPreview} - onClick={() => openImageInNewTab(url)} style={{ width: thumbW, height: thumbH, @@ -119,11 +170,14 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { border: '1px solid #ddd', cursor: 'zoom-in', }} + onMouseEnter={(event) => showImageHoverPreview(url, event)} + onMouseMove={(event) => showImageHoverPreview(url, event)} + onMouseLeave={hideImageHoverPreview} + onClick={() => openImageInNewTab(url)} /> @@ -161,7 +216,6 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { type="button" title="Görsel Yükle" disabled={uploading} - onClick={() => inputRef.current?.click()} variant="solid" shape="none" className="!h-auto !rounded !px-2.5 !py-1 !text-[13px]" @@ -177,6 +231,7 @@ const ImageUploadEditorComponent = (cellElement: any): ReactElement => { alignItems: 'center', gap: 4, }} + onClick={() => inputRef.current?.click()} > {uploading ? ( diff --git a/ui/src/views/list/useFilters.tsx b/ui/src/views/list/useFilters.tsx index ed246f8..e9c1aef 100644 --- a/ui/src/views/list/useFilters.tsx +++ b/ui/src/views/list/useFilters.tsx @@ -57,10 +57,6 @@ const isTreeList = (grid: GridInstance): grid is dxTreeList => { return 'getRootNode' in grid } -const isGantt = (grid: GridInstance): grid is dxGantt => { - return 'getTaskData' in grid -} - const supportsState = (grid: GridInstance): grid is dxDataGrid | dxTreeList => { return 'state' in grid && typeof (grid as any).state === 'function' } @@ -340,7 +336,7 @@ const useFilters = ({ const [isPermissionUsersModalOpen, setIsPermissionUsersModalOpen] = useState(false) const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk - const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk + //const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk const grdOpt = gridDto?.gridOptions const config = useStoreState((state) => state.abpConfig.config) diff --git a/ui/src/views/list/useListFormColumns.ts b/ui/src/views/list/useListFormColumns.ts index 73e4b2a..fa55386 100644 --- a/ui/src/views/list/useListFormColumns.ts +++ b/ui/src/views/list/useListFormColumns.ts @@ -9,7 +9,6 @@ import { usePWA } from '@/utils/hooks/usePWA' import { useDialogContext } from '../shared/DialogContext' import { dynamicFetch } from '@/services/form.service' import { GridColumnData } from './GridColumnData' -import { useLocation } from 'react-router-dom' import { ColumnFormatDto, EditingFormItemDto, @@ -381,7 +380,6 @@ const useListFormColumns = ({ const { translate } = useLocalization() const { checkPermission } = usePermission() const isPwaMode = usePWA() - const location = useLocation() useEffect(() => { // listFormCode değişince lookup cache temizlensin (farklı form farklı lookuplar) @@ -488,7 +486,7 @@ const useListFormColumns = ({ store: new CustomStore({ key, loadMode: 'raw', - load: async (loadOptions) => { + load: async () => { // load fonksiyonu sync sonuç döndürüyor olabilir, o yüzden Promise.resolve ile sar return cachedLoader(`static:${key}`, () => Promise.resolve(load())) }, @@ -504,7 +502,7 @@ const useListFormColumns = ({ ) => { return new CustomStore({ loadMode: 'raw', - load: async (loadOptions) => { + load: async () => { if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) { return } @@ -526,7 +524,7 @@ const useListFormColumns = ({ ...a, })) }) - } catch (error: any) { + } catch { return null } }, @@ -542,7 +540,7 @@ const useListFormColumns = ({ return new CustomStore({ key: keyName, loadMode: 'raw', - load: async (loadOptions) => { + load: async () => { if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) { return } @@ -713,7 +711,7 @@ const useListFormColumns = ({ if (gridRef?.current?.instance()) { gridRef.current.instance().refresh() } - } catch (err) { + } catch { // Hata yönetimi alert(translate('::App.Platform.DuplicateError')) } diff --git a/ui/src/views/list/useListFormCustomDataSource.ts b/ui/src/views/list/useListFormCustomDataSource.ts index f37020c..db4aa7a 100644 --- a/ui/src/views/list/useListFormCustomDataSource.ts +++ b/ui/src/views/list/useListFormCustomDataSource.ts @@ -290,7 +290,7 @@ const useListFormCustomDataSource = ({ } return retValue - } catch (error: any) { + } catch { // toast.push( // // Select error @@ -334,7 +334,7 @@ const useListFormCustomDataSource = ({ try { const response = await dynamicFetch('list-form-select/select', 'GET', parameters) return response.data.totalCount - } catch (error: any) { + } catch { // toast.push( // // TotalCount error @@ -357,7 +357,7 @@ const useListFormCustomDataSource = ({ try { const response = await dynamicFetch('list-form-select/select', 'GET', parameters) return response.data.data[0] - } catch (error: any) { + } catch { // toast.push( // // ByKey error diff --git a/ui/src/views/menu/MenuItemComponent.tsx b/ui/src/views/menu/MenuItemComponent.tsx index ebbf1de..0535365 100644 --- a/ui/src/views/menu/MenuItemComponent.tsx +++ b/ui/src/views/menu/MenuItemComponent.tsx @@ -5,7 +5,6 @@ import { MenuItem } from '@/proxy/menus/menu' import { useLocalization } from '@/utils/hooks/useLocalization' import { MenuService } from '@/services/menu.service' import navigationIcon from '@/proxy/menus/navigation-icon.config' -import { FaQuestionCircle } from 'react-icons/fa' import { Button, Dialog, @@ -20,7 +19,7 @@ import { import { Field, FieldProps, Form, Formik } from 'formik' import { SelectBoxOption } from '@/types/shared' import * as Yup from 'yup' -import { FaExternalLinkAlt, FaPlus, FaTrashAlt } from 'react-icons/fa' +import { FaQuestionCircle, FaPlus, FaTrashAlt } from 'react-icons/fa' import { MenuDto } from '@/proxy/menus/models' interface MenuItemComponentProps { @@ -256,6 +255,7 @@ export const MenuItemComponent: React.FC = ({ setIsModalOpen(false) refetch() } catch (error) { + console.error('Failed to save menu item:', error) toast.push( {translate('::IslemBasarisiz')} diff --git a/ui/src/views/menu/MenuManager.tsx b/ui/src/views/menu/MenuManager.tsx index dbde9a5..ae82146 100644 --- a/ui/src/views/menu/MenuManager.tsx +++ b/ui/src/views/menu/MenuManager.tsx @@ -1,6 +1,5 @@ -import React, { useState } from 'react' +import { useState } from 'react' import { SortableMenuTree } from './SortableMenuTree' -import { MenuItem } from '@/proxy/menus/menu' import { useMenuData } from '@/utils/hooks/useMenuData' import { FaRegBell, FaSpinner, FaBars, FaRegSave } from 'react-icons/fa' import { Container } from '@/components/shared' @@ -15,40 +14,23 @@ export const MenuManager = () => { const [isSaving, setIsSaving] = useState(false) const { translate } = useLocalization() - const [saveMessage, setSaveMessage] = useState<{ - type: 'success' | 'error' - text: string - } | null>(null) - - const handleMenuChange = (updatedItems: MenuItem[]) => { - setMenuItems(updatedItems) - } - const handleSave = async () => { if (!isDesignMode) return try { setIsSaving(true) - setSaveMessage(null) await saveMenuData(menuItems) - setSaveMessage({ type: 'success', text: 'Menu configuration saved successfully!' }) - setTimeout(() => setSaveMessage(null), 3000) setIsDesignMode(false) } catch (err) { - setSaveMessage({ - type: 'error', - text: err instanceof Error ? err.message : 'Failed to save menu configuration', - }) - setTimeout(() => setSaveMessage(null), 5000) + console.error('Failed to save menu configuration:', err) } finally { setIsSaving(false) } } const handleToggleDesignMode = () => { - setIsDesignMode(!isDesignMode) - setSaveMessage(null) + setIsDesignMode((current) => !current) } if (loading) { @@ -72,10 +54,10 @@ export const MenuManager = () => {

{error}

@@ -90,7 +72,7 @@ export const MenuManager = () => { titleTemplate={`%s | ${APP_NAME}`} title={translate('::' + 'App.Menus.Manager')} defaultTitle={APP_NAME} - > + />
@@ -114,13 +96,13 @@ export const MenuManager = () => { Design Mode
- { - - } + onClick={handleSave} + > + {isSaving ? ( + <> + + {translate('::Saving')} + + ) : ( + <> + + {translate('::App.SaveChanges')} + + )} +
{menuItems.length > 0 ? ( ) : (
diff --git a/ui/src/views/public/About.tsx b/ui/src/views/public/About.tsx index 2d44e36..ea21c63 100644 --- a/ui/src/views/public/About.tsx +++ b/ui/src/views/public/About.tsx @@ -8,8 +8,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import Loading from '@/components/shared/Loading' import { APP_NAME } from '@/constants/app.constant' import { Button, Notification, toast } from '@/components/ui' -import { useStoreState } from '@/store' -import { useStoreActions } from '@/store' +import { useStoreState, useStoreActions } from '@/store' import DesignerDrawer from './designer/DesignerDrawer' import SelectableBlock from './designer/SelectableBlock' import { DesignerSelection } from './designer/types' diff --git a/ui/src/views/public/Home.tsx b/ui/src/views/public/Home.tsx index 7030e55..57f20d4 100644 --- a/ui/src/views/public/Home.tsx +++ b/ui/src/views/public/Home.tsx @@ -839,13 +839,13 @@ const Home: React.FC = () => { ctaButtonLabelValue: content.ctaButtonLabel, ctaButtonStyleKey: content.ctaButtonStyleKey, ctaButtonStyleValue: content.ctaButtonStyle, - slides: content.slides.map((slide, slideIndex) => ({ + slides: content.slides.map((slide) => ({ titleKey: slide.titleKey || '', titleValue: slide.title, subtitleKey: slide.subtitleKey || '', subtitleValue: slide.subtitle, styleClass: slide.styleClass, - services: slide.services.map((service, serviceIndex) => ({ + services: slide.services.map((service) => ({ icon: service.icon, titleKey: service.titleKey || '', titleValue: service.title, @@ -854,7 +854,7 @@ const Home: React.FC = () => { styleClass: service.styleClass, })), })), - features: content.features.map((feature, index) => ({ + features: content.features.map((feature) => ({ icon: feature.icon, titleKey: feature.titleKey || '', titleValue: feature.title, @@ -862,7 +862,7 @@ const Home: React.FC = () => { descriptionValue: feature.description, styleClass: feature.styleClass, })), - solutions: content.solutions.map((solution, index) => ({ + solutions: content.solutions.map((solution) => ({ icon: solution.icon, colorClass: solution.colorClass, titleKey: solution.titleKey || '', diff --git a/ui/src/views/public/Services.tsx b/ui/src/views/public/Services.tsx index 5a2581d..ab944bb 100644 --- a/ui/src/views/public/Services.tsx +++ b/ui/src/views/public/Services.tsx @@ -9,8 +9,7 @@ import { getServices, saveServicesPage } from '@/services/service.service' import navigationIcon from '@/proxy/menus/navigation-icon.config' import { Loading } from '@/components/shared' import { APP_NAME } from '@/constants/app.constant' -import { useStoreState } from '@/store' -import { useStoreActions } from '@/store' +import { useStoreState, useStoreActions } from '@/store' import DesignerDrawer from './designer/DesignerDrawer' import SelectableBlock from './designer/SelectableBlock' import { DesignerSelection } from './designer/types' diff --git a/ui/src/views/public/designer/DesignerDrawer.tsx b/ui/src/views/public/designer/DesignerDrawer.tsx index 1599237..3f0b911 100644 --- a/ui/src/views/public/designer/DesignerDrawer.tsx +++ b/ui/src/views/public/designer/DesignerDrawer.tsx @@ -1,3 +1,4 @@ +import React from 'react' import Avatar from '@/components/ui/Avatar' import { Button, Input } from '@/components/ui' import { IconPickerField } from '@/views/shared/MenuAddDialog' @@ -40,7 +41,6 @@ const DesignerDrawer: React.FC = ({ onClose, onSave, onLanguageChange, - onReset, onFieldChange, }) => { const { translate } = useLocalization() diff --git a/ui/src/views/public/designer/SelectableBlock.tsx b/ui/src/views/public/designer/SelectableBlock.tsx index b4d93f1..e72e2c9 100644 --- a/ui/src/views/public/designer/SelectableBlock.tsx +++ b/ui/src/views/public/designer/SelectableBlock.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames' -import { PropsWithChildren } from 'react' +import React, { PropsWithChildren } from 'react' interface SelectableBlockProps extends PropsWithChildren { id: string diff --git a/ui/src/views/report/DevexpressReportDesigner.tsx b/ui/src/views/report/DevexpressReportDesigner.tsx index 9f7e20f..629dfc6 100644 --- a/ui/src/views/report/DevexpressReportDesigner.tsx +++ b/ui/src/views/report/DevexpressReportDesigner.tsx @@ -14,7 +14,6 @@ const ReportDesigner = lazy(() => ) const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' -const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides' const REPORT_DESIGNER_CSS_ID = 'devexpress-report-designer-css' const getViewerClassName = (isDarkMode: boolean) => diff --git a/ui/src/views/report/DevexpressReportViewer.tsx b/ui/src/views/report/DevexpressReportViewer.tsx index 01e3dbf..68080d1 100644 --- a/ui/src/views/report/DevexpressReportViewer.tsx +++ b/ui/src/views/report/DevexpressReportViewer.tsx @@ -1,4 +1,5 @@ -import React, { useMemo, useEffect, useState, useCallback, useRef } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import type { FC } from 'react' import { Container } from '@/components/shared' import { Helmet } from 'react-helmet' import { useLocalization } from '@/utils/hooks/useLocalization' @@ -15,9 +16,24 @@ import { useStoreState } from '@/store' import { createReportQueryString, createReportUrl } from './reportRouteParams' const VIEWER_THEME_LINK_ID = 'devexpress-report-viewer-theme' -const VIEWER_DARK_OVERRIDES_ID = 'devexpress-report-viewer-dark-overrides' const DEFAULT_REPORT_ZOOM = 1 +interface ReportPreviewModel { + autoFitBy?: ZoomAutoBy + originalZoom?: number + zoom?: number +} + +interface ReportPreviewContainer { + reportPreview?: ReportPreviewModel + ReportPreview?: ReportPreviewModel + previewModel?: ReportPreviewContainer + args?: ReportPreviewContainer + sender?: ReportPreviewContainer & { + GetReportPreview?: () => ReportPreviewModel + } +} + const getViewerClassName = (isDarkMode: boolean) => [ 'dx-viewport', @@ -62,7 +78,7 @@ const loadViewerCss = (isDarkMode: boolean) => { } } -const DevexpressReportViewer: React.FC = () => { +const DevexpressReportViewer: FC = () => { const { translate } = useLocalization() const { report, listFormCode, id } = useParams<{ report: string @@ -76,35 +92,42 @@ const DevexpressReportViewer: React.FC = () => { const configCultureName = useStoreState( (state) => state.abpConfig.config?.localization.currentCulture.cultureName, ) - const [isRequestAuthReady, setIsRequestAuthReady] = useState(false) - const previewModelRef = useRef(null) + const [configuredRequestKey, setConfiguredRequestKey] = useState() + const previewModelRef = useRef() const isDarkMode = themeMode === MODE_DARK const reportCultureName = cultureName || configCultureName + const requestConfigKey = `${token ?? ''}\u0000${reportCultureName ?? ''}` - const getReportPreview = useCallback((eventOrModel?: any) => { - const eventArgs = eventOrModel?.args - const sender = eventOrModel?.sender - const candidates = [ - eventArgs?.reportPreview, - eventArgs?.ReportPreview, - eventArgs?.previewModel?.reportPreview, - eventOrModel?.reportPreview, - eventOrModel?.ReportPreview, - eventOrModel?.previewModel?.reportPreview, - sender?.GetReportPreview?.(), - sender?.previewModel?.reportPreview, - previewModelRef.current, - ] + const getReportPreview = useCallback( + (eventOrModel?: ReportPreviewContainer | ReportPreviewModel) => { + const event = eventOrModel as ReportPreviewContainer | undefined + const eventArgs = event?.args + const sender = event?.sender + const previewModel = event?.previewModel + const candidates = [ + eventArgs?.reportPreview, + eventArgs?.ReportPreview, + eventArgs?.previewModel?.reportPreview, + event?.reportPreview, + event?.ReportPreview, + previewModel?.reportPreview, + sender?.GetReportPreview?.(), + sender?.previewModel?.reportPreview, + eventOrModel as ReportPreviewModel | undefined, + previewModelRef.current, + ] - return candidates.find( - (candidate) => - candidate && - ('zoom' in candidate || 'originalZoom' in candidate || 'autoFitBy' in candidate), - ) - }, []) + return candidates.find( + (candidate) => + candidate && + ('zoom' in candidate || 'originalZoom' in candidate || 'autoFitBy' in candidate), + ) + }, + [], + ) const setDefaultZoom = useCallback( - (eventOrModel?: any) => { + (eventOrModel?: ReportPreviewContainer | ReportPreviewModel) => { const preview = getReportPreview(eventOrModel) if (!preview) return @@ -121,32 +144,17 @@ const DevexpressReportViewer: React.FC = () => { }, [isDarkMode]) useEffect(() => { - setIsRequestAuthReady(false) - const authorization = token ? `Bearer ${token}` : undefined + const nextHeaders = { ...(ajaxSetup.ajaxSettings.headers ?? {}) } + delete nextHeaders.Authorization + delete nextHeaders['Accept-Language'] - const ajaxHeaders = ajaxSetup.ajaxSettings.headers ?? {} - - if (authorization) { - ajaxSetup.ajaxSettings.headers = { - ...ajaxHeaders, - Authorization: authorization, - ...(reportCultureName ? { 'Accept-Language': reportCultureName } : {}), - } - setIsRequestAuthReady(true) - return + ajaxSetup.ajaxSettings.headers = { + ...nextHeaders, + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(reportCultureName ? { 'Accept-Language': reportCultureName } : {}), } - - const { - Authorization: _ajaxAuthorization, - 'Accept-Language': _ajaxAcceptLanguage, - ...nextAjaxHeaders - } = ajaxHeaders - if (reportCultureName) { - nextAjaxHeaders['Accept-Language'] = reportCultureName - } - ajaxSetup.ajaxSettings.headers = nextAjaxHeaders - setIsRequestAuthReady(true) - }, [token, reportCultureName]) + setConfiguredRequestKey(requestConfigKey) + }, [token, reportCultureName, requestConfigKey]) const reportUrlWithParams = useMemo(() => { const searchParams = new URLSearchParams(location.search) @@ -168,12 +176,9 @@ const DevexpressReportViewer: React.FC = () => { defaultTitle={APP_NAME} /> - {isRequestAuthReady && ( + {configuredRequestKey === requestConfigKey && ( - + )} diff --git a/ui/src/views/settings/Settings.tsx b/ui/src/views/settings/Settings.tsx index 22223f8..85de773 100644 --- a/ui/src/views/settings/Settings.tsx +++ b/ui/src/views/settings/Settings.tsx @@ -166,7 +166,7 @@ const Settings = () => { }, 1000) }} > - {({ values, touched, errors, isSubmitting }) => ( + {({ values, isSubmitting }) => ( {subGroup.settings.map((setting) => ( diff --git a/ui/src/views/shared/FormRow.tsx b/ui/src/views/shared/FormRow.tsx index 9eb77d0..1a1d205 100644 --- a/ui/src/views/shared/FormRow.tsx +++ b/ui/src/views/shared/FormRow.tsx @@ -3,7 +3,7 @@ import { FormItem } from '@/components/ui/Form' import type { PropsWithChildren } from 'react' import type { FormikTouched, FormikErrors } from 'formik' -type FormRow = PropsWithChildren<{ +type FormRowProps = PropsWithChildren<{ label: string errors: FormikErrors touched: FormikTouched @@ -12,7 +12,7 @@ type FormRow = PropsWithChildren<{ alignCenter?: boolean }> -const FormRow = >(props: FormRow) => { +const FormRow = >(props: FormRowProps) => { const { label, children, diff --git a/ui/src/views/version/ChangeLog.tsx b/ui/src/views/version/ChangeLog.tsx index 286a88b..bd94323 100644 --- a/ui/src/views/version/ChangeLog.tsx +++ b/ui/src/views/version/ChangeLog.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react" +import React, { useEffect, useState } from "react" import AdaptableCard from "@/components/shared/AdaptableCard" import Container from "@/components/shared/Container" import { FaTag, FaRegCheckCircle } from "react-icons/fa"