diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.json
index e1ff1d56..8ebb70cd 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/HostData.json
@@ -12,7 +12,7 @@
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
"descriptionKey": "App.GeneralNewMemberNotificationEmails.NewMemberNotificationEmailsDescription",
- "defaultValue": "system@sozsoft.com",
+ "defaultValue": "destek@sozsoft.com",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
@@ -28,7 +28,7 @@
"code": "App.SiteManagement.General.TimedLoginEmails",
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
"descriptionKey": "App.GeneralTimedLoginEmails.TimedLoginEmailsDescription",
- "defaultValue": "system@sozsoft.com",
+ "defaultValue": "destek@sozsoft.com",
"isVisibleToClients": false,
"providers": "T|G|D",
"isInherited": false,
diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
index 3e3fcf93..113abbad 100644
--- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
+++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/LanguagesData.json
@@ -6846,6 +6846,12 @@
"tr": "Bültene Abone Ol",
"en": "Subscribe to Newsletter"
},
+ {
+ "resourceName": "Platform",
+ "key": "App.PublicBlog.SearchPlaceholder",
+ "en": "Search blog posts...",
+ "tr": "Blog yazıları ara..."
+ },
{
"resourceName": "Platform",
"key": "App.BlogSubscribe.SubscribeDesc",
diff --git a/ui/package-lock.json b/ui/package-lock.json
index fc45f660..ba6a8f52 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -11,14 +11,9 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@floating-ui/react": "^0.27.20",
- "@fullcalendar/daygrid": "^6.1.21",
- "@fullcalendar/interaction": "^6.1.21",
- "@fullcalendar/react": "^6.1.21",
- "@fullcalendar/timegrid": "^6.1.21",
"@marsidev/react-turnstile": "^1.6.0",
"@microsoft/signalr": "^10.0.11",
"@monaco-editor/react": "^4.7.0",
- "@tanstack/react-query": "^5.101.4",
"@tanstack/react-table": "^8.21.3",
"axios": "^1.19.0",
"classnames": "^2.5.1",
@@ -33,30 +28,23 @@
"formik": "^2.4.9",
"framer-motion": "^13.1.0",
"jspdf": "^4.2.1",
- "jwt-decode": "^4.0.0",
- "lodash": "^4.18.1",
"react": "^19.2.8",
"react-advanced-cropper": "^0.20.1",
"react-arborist": "^3.16.0",
"react-dom": "^19.2.8",
- "react-error-boundary": "^6.1.2",
- "react-highlight-words": "^0.21.0",
"react-icons": "^5.7.0",
"react-modal": "^3.16.3",
"react-router-dom": "^7.18.2",
"react-select": "^5.10.2",
- "yet-another-react-lightbox": "^3.32.2",
"yup": "^1.7.1"
},
"devDependencies": {
"@eslint/js": "^9.39.5",
"@tailwindcss/typography": "^0.5.20",
"@types/file-saver": "^2.0.7",
- "@types/lodash": "^4.17.25",
"@types/node": "^24.10.1",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
- "@types/react-highlight-words": "^0.20.1",
"@types/react-modal": "^3.16.3",
"@typescript-eslint/eslint-plugin": "^8.67.0",
"@typescript-eslint/parser": "^8.67.0",
@@ -2182,57 +2170,6 @@
"integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
"license": "MIT"
},
- "node_modules/@fullcalendar/core": {
- "version": "6.1.21",
- "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.21.tgz",
- "integrity": "sha512-t3u/+sqh3Iq7TWtUnVLcGDUE6OWZh0UD3c04bI/l7lSLAgAKr3kngBmhHiQD1QXpwC8ZN5iNqG7a7gOVixhSKQ==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "preact": "~10.12.1"
- }
- },
- "node_modules/@fullcalendar/daygrid": {
- "version": "6.1.21",
- "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.21.tgz",
- "integrity": "sha512-QYb1y40RGYLlOxKpYWg8O+7njEnKnFG8Tt7qjnubJGR35s1phQg67E+81y2TyAbbm59p2JFOCXGDk9t6KDujIA==",
- "license": "MIT",
- "peerDependencies": {
- "@fullcalendar/core": "~6.1.21"
- }
- },
- "node_modules/@fullcalendar/interaction": {
- "version": "6.1.21",
- "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.21.tgz",
- "integrity": "sha512-WPYpqtljDWmU0Xm2cOtFrLlocgxv7cgkOppj34Q6OUUat8a6Cnd6kYo2JR+irP223PE5lBYHFNp1qh7SIpJc0w==",
- "license": "MIT",
- "peerDependencies": {
- "@fullcalendar/core": "~6.1.21"
- }
- },
- "node_modules/@fullcalendar/react": {
- "version": "6.1.21",
- "resolved": "https://registry.npmjs.org/@fullcalendar/react/-/react-6.1.21.tgz",
- "integrity": "sha512-TLpmGUd5k/PMdCh8XbeFC9PW9wuGvMms1oCxWgXyjK3EFPXAAd0PLfcvwKdyxoAS5eK1E4RJFkjMHvsYHpimcg==",
- "license": "MIT",
- "peerDependencies": {
- "@fullcalendar/core": "~6.1.21",
- "react": "^16.7.0 || ^17 || ^18 || ^19",
- "react-dom": "^16.7.0 || ^17 || ^18 || ^19"
- }
- },
- "node_modules/@fullcalendar/timegrid": {
- "version": "6.1.21",
- "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.21.tgz",
- "integrity": "sha512-2DnShx/jallGmb8QCkr6pAOu/zuPhJrP7+uTrAtSnbqsX7GF3lTxqSeNGkTQwsgF5g/ia8udhQ+JNYaE+TN1cQ==",
- "license": "MIT",
- "dependencies": {
- "@fullcalendar/daygrid": "~6.1.21"
- },
- "peerDependencies": {
- "@fullcalendar/core": "~6.1.21"
- }
- },
"node_modules/@humanfs/core": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
@@ -3266,32 +3203,6 @@
"tailwindcss": ">=3.0.0 || >=4.0.0 || insiders"
}
},
- "node_modules/@tanstack/query-core": {
- "version": "5.101.4",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz",
- "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/react-query": {
- "version": "5.101.4",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz",
- "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==",
- "license": "MIT",
- "dependencies": {
- "@tanstack/query-core": "5.101.4"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^18 || ^19"
- }
- },
"node_modules/@tanstack/react-table": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
@@ -3402,13 +3313,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/lodash": {
- "version": "4.17.25",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz",
- "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/node": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
@@ -3463,16 +3367,6 @@
"@types/react": "^19.2.0"
}
},
- "node_modules/@types/react-highlight-words": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@types/react-highlight-words/-/react-highlight-words-0.20.1.tgz",
- "integrity": "sha512-EgF6RaoibBurIhxk3X3d5xL0uMqjD7KtShvZ8S+d7/o6xZ+mQ4rct1a2E49vWA9r63OzrHCIyghqvQHASb/ERw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/react": "*"
- }
- },
"node_modules/@types/react-modal": {
"version": "3.16.3",
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz",
@@ -7669,12 +7563,6 @@
"hermes-estree": "0.25.1"
}
},
- "node_modules/highlight-words-core": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz",
- "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==",
- "license": "MIT"
- },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -8590,15 +8478,6 @@
"safe-buffer": "~5.1.0"
}
},
- "node_modules/jwt-decode": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
- "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -10383,17 +10262,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/preact": {
- "version": "10.12.1",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
- "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
- "license": "MIT",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/preact"
- }
- },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -10647,34 +10515,12 @@
"react": "^19.2.8"
}
},
- "node_modules/react-error-boundary": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.2.tgz",
- "integrity": "sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==",
- "license": "MIT",
- "peerDependencies": {
- "react": "^18.0.0 || ^19.0.0"
- }
- },
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
"license": "MIT"
},
- "node_modules/react-highlight-words": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/react-highlight-words/-/react-highlight-words-0.21.0.tgz",
- "integrity": "sha512-SdWEeU9fIINArEPO1rO5OxPyuhdEKZQhHzZZP1ie6UeXQf+CjycT1kWaB+9bwGcVbR0NowuHK3RqgqNg6bgBDQ==",
- "license": "MIT",
- "dependencies": {
- "highlight-words-core": "^1.2.0",
- "memoize-one": "^4.0.0"
- },
- "peerDependencies": {
- "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0"
- }
- },
"node_modules/react-icons": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz",
@@ -13240,32 +13086,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/yet-another-react-lightbox": {
- "version": "3.32.2",
- "resolved": "https://registry.npmjs.org/yet-another-react-lightbox/-/yet-another-react-lightbox-3.32.2.tgz",
- "integrity": "sha512-F4HtHQfUNpvkj+AmECgWM4XRdCqMY5gXpKgOUx39+T+FyxLe8II4SK/pwMyYj2X54KH9lFSQeHYY1/GfYf3SdA==",
- "license": "MIT",
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/igordanchenko"
- },
- "peerDependencies": {
- "@types/react": "^16 || ^17 || ^18 || ^19",
- "@types/react-dom": "^16 || ^17 || ^18 || ^19",
- "react": "^16.8.0 || ^17 || ^18 || ^19",
- "react-dom": "^16.8.0 || ^17 || ^18 || ^19"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
- }
- },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/ui/package.json b/ui/package.json
index 647de932..475392f2 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -29,14 +29,9 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@floating-ui/react": "^0.27.20",
- "@fullcalendar/daygrid": "^6.1.21",
- "@fullcalendar/interaction": "^6.1.21",
- "@fullcalendar/react": "^6.1.21",
- "@fullcalendar/timegrid": "^6.1.21",
"@marsidev/react-turnstile": "^1.6.0",
"@microsoft/signalr": "^10.0.11",
"@monaco-editor/react": "^4.7.0",
- "@tanstack/react-query": "^5.101.4",
"@tanstack/react-table": "^8.21.3",
"axios": "^1.19.0",
"classnames": "^2.5.1",
@@ -51,30 +46,23 @@
"formik": "^2.4.9",
"framer-motion": "^13.1.0",
"jspdf": "^4.2.1",
- "jwt-decode": "^4.0.0",
- "lodash": "^4.18.1",
"react": "^19.2.8",
"react-advanced-cropper": "^0.20.1",
"react-arborist": "^3.16.0",
"react-dom": "^19.2.8",
- "react-error-boundary": "^6.1.2",
- "react-highlight-words": "^0.21.0",
"react-icons": "^5.7.0",
"react-modal": "^3.16.3",
"react-router-dom": "^7.18.2",
"react-select": "^5.10.2",
- "yet-another-react-lightbox": "^3.32.2",
"yup": "^1.7.1"
},
"devDependencies": {
"@eslint/js": "^9.39.5",
"@tailwindcss/typography": "^0.5.20",
"@types/file-saver": "^2.0.7",
- "@types/lodash": "^4.17.25",
"@types/node": "^24.10.1",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
- "@types/react-highlight-words": "^0.20.1",
"@types/react-modal": "^3.16.3",
"@typescript-eslint/eslint-plugin": "^8.67.0",
"@typescript-eslint/parser": "^8.67.0",
diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index 7985ec86..f8d8f810 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -1,6 +1,5 @@
import Layout from '@/components/layouts'
import Theme from '@/components/template/Theme'
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { StoreProvider } from 'easy-peasy'
import { BrowserRouter } from 'react-router-dom'
import { store } from './store'
@@ -10,15 +9,6 @@ import { registerServiceWorker } from './views/version/swRegistration'
import { useEffect } from 'react'
import DevExtremeBoundary from './components/devExtreme/DevExtremeBoundary'
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- staleTime: 30_000,
- refetchOnWindowFocus: false,
- },
- },
-})
-
function App() {
useEffect(() => {
registerServiceWorker()
@@ -30,13 +20,11 @@ function App() {
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/ui/src/components/shared/CalendarView.tsx b/ui/src/components/shared/CalendarView.tsx
deleted file mode 100644
index 6ca87db3..00000000
--- a/ui/src/components/shared/CalendarView.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-import classNames from 'classnames'
-import Badge from '@/components/ui/Badge'
-import FullCalendar from '@fullcalendar/react'
-import dayGridPlugin from '@fullcalendar/daygrid'
-import timeGridPlugin from '@fullcalendar/timegrid'
-import interactionPlugin from '@fullcalendar/interaction'
-import { CalendarOptions } from '@fullcalendar/core'
-
-type EventColors = Record<
- string,
- {
- bg: string
- text: string
- dot: string
- }
->
-
-interface CalendarViewProps extends CalendarOptions {
- wrapperClass?: string
- eventColors?: (colors: EventColors) => EventColors
-}
-
-const defaultColorList: Record<
- string,
- {
- bg: string
- text: string
- dot: string
- }
-> = {
- red: {
- bg: 'bg-red-50 dark:bg-red-500/10',
- text: 'text-red-500 dark:text-red-100',
- dot: 'bg-red-500',
- },
- orange: {
- bg: 'bg-orange-50 dark:bg-orange-500/10',
- text: 'text-orange-500 dark:text-orange-100',
- dot: 'bg-orange-500',
- },
- amber: {
- bg: 'bg-amber-50 dark:bg-amber-500/10',
- text: 'text-amber-500 dark:text-amber-100',
- dot: 'bg-amber-500',
- },
- yellow: {
- bg: 'bg-yellow-50 dark:bg-yellow-500/10',
- text: 'text-yellow-500 dark:text-yellow-100',
- dot: 'bg-yellow-500',
- },
- lime: {
- bg: 'bg-lime-50 dark:bg-lime-500/10',
- text: 'text-lime-500 dark:text-lime-100',
- dot: 'bg-lime-500',
- },
- green: {
- bg: 'bg-green-50 dark:bg-green-500/10',
- text: 'text-green-500 dark:text-green-100',
- dot: 'bg-green-500',
- },
- emerald: {
- bg: 'bg-emerald-50 dark:bg-emerald-500/10',
- text: 'text-emerald-500 dark:text-emerald-100',
- dot: 'bg-emerald-500',
- },
- teal: {
- bg: 'bg-teal-50 dark:bg-teal-500/10',
- text: 'text-teal-500 dark:text-teal-100',
- dot: 'bg-teal-500',
- },
- cyan: {
- bg: 'bg-cyan-50 dark:bg-cyan-500/10',
- text: 'text-cyan-500 dark:text-cyan-100',
- dot: 'bg-cyan-500',
- },
- sky: {
- bg: 'bg-sky-50 dark:bg-sky-500/10',
- text: 'text-sky-500 dark:text-sky-100',
- dot: 'bg-sky-500',
- },
- blue: {
- bg: 'bg-blue-50 dark:bg-blue-500/10',
- text: 'text-blue-500 dark:text-blue-100',
- dot: 'bg-blue-500',
- },
- indigo: {
- bg: 'bg-indigo-50 dark:bg-indigo-500/10',
- text: 'text-indigo-500 dark:text-indigo-100',
- dot: 'bg-indigo-500',
- },
- purple: {
- bg: 'bg-purple-50 dark:bg-purple-500/10',
- text: 'text-purple-500 dark:text-purple-100',
- dot: 'bg-purple-500',
- },
- fuchsia: {
- bg: 'bg-fuchsia-50 dark:bg-fuchsia-500/10',
- text: 'text-fuchsia-500 dark:text-fuchsia-100',
- dot: 'bg-fuchsia-500',
- },
- pink: {
- bg: 'bg-pink-50 dark:bg-pink-500/10',
- text: 'text-pink-500 dark:text-pink-100',
- dot: 'bg-pink-500',
- },
- rose: {
- bg: 'bg-rose-50 dark:bg-rose-500/10',
- text: 'text-rose-500 dark:text-rose-100',
- dot: 'bg-rose-500',
- },
-}
-
-const CalendarView = (props: CalendarViewProps) => {
- const {
- wrapperClass,
- eventColors = () => defaultColorList,
- ...rest
- } = props
-
- return (
-
-
{
- const { extendedProps } = arg.event
- const { isEnd, isStart } = arg
- return (
-
- {!(isEnd && !isStart) && (
-
- )}
- {!(isEnd && !isStart) && (
- {arg.timeText}
- )}
-
- {arg.event.title}
-
-
- )
- }}
- plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
- {...rest}
- />
-
- )
-}
-
-export default CalendarView
diff --git a/ui/src/components/shared/ErrorBoundary.tsx b/ui/src/components/shared/ErrorBoundary.tsx
new file mode 100644
index 00000000..b20aca14
--- /dev/null
+++ b/ui/src/components/shared/ErrorBoundary.tsx
@@ -0,0 +1,62 @@
+import { Component } from 'react'
+import type { ComponentType, ErrorInfo, ReactNode } from 'react'
+
+export interface FallbackProps {
+ error: unknown
+ resetErrorBoundary: () => void
+}
+
+interface ErrorBoundaryProps {
+ FallbackComponent: ComponentType
+ children: ReactNode
+ /** Bu değerler değiştiğinde sınır kendini sıfırlar (ör. route değişimi). */
+ resetKeys?: unknown[]
+ onError?: (error: unknown, info: ErrorInfo) => void
+}
+
+interface ErrorBoundaryState {
+ error: unknown
+ hasError: boolean
+}
+
+const areKeysEqual = (a: unknown[] = [], b: unknown[] = []) =>
+ a.length === b.length && a.every((item, index) => Object.is(item, b[index]))
+
+/**
+ * react-error-boundary yerine kullanılan yerel sınır.
+ * Uygulamanın ihtiyaç duyduğu yüzey kadarı var: FallbackComponent,
+ * resetErrorBoundary ve resetKeys.
+ */
+class ErrorBoundary extends Component {
+ state: ErrorBoundaryState = { error: null, hasError: false }
+
+ static getDerivedStateFromError(error: unknown): ErrorBoundaryState {
+ return { error, hasError: true }
+ }
+
+ componentDidCatch(error: unknown, info: ErrorInfo) {
+ this.props.onError?.(error, info)
+ }
+
+ componentDidUpdate(prevProps: ErrorBoundaryProps) {
+ if (this.state.hasError && !areKeysEqual(prevProps.resetKeys, this.props.resetKeys)) {
+ this.reset()
+ }
+ }
+
+ reset = () => {
+ this.setState({ error: null, hasError: false })
+ }
+
+ render() {
+ const { FallbackComponent, children } = this.props
+
+ if (this.state.hasError) {
+ return
+ }
+
+ return children
+ }
+}
+
+export default ErrorBoundary
diff --git a/ui/src/components/shared/Highlighter.tsx b/ui/src/components/shared/Highlighter.tsx
new file mode 100644
index 00000000..bf90591e
--- /dev/null
+++ b/ui/src/components/shared/Highlighter.tsx
@@ -0,0 +1,44 @@
+import { useMemo, Fragment } from 'react'
+
+interface HighlighterProps {
+ /** Vurgulanacak kelimeler; boş/whitespace olanlar yok sayılır. */
+ searchWords: string[]
+ textToHighlight: string
+ highlightClassName?: string
+}
+
+const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+
+/**
+ * react-highlight-words yerine kullanılan yerel bileşen.
+ * Kullanılan tek senaryo: arama kelimelerini büyük/küçük harf duyarsız
+ * eşleyip ile işaretlemek (paketteki `autoEscape` davranışı varsayılan).
+ */
+const Highlighter = ({ searchWords, textToHighlight, highlightClassName }: HighlighterProps) => {
+ const parts = useMemo(() => {
+ const words = searchWords.map((word) => word?.trim()).filter(Boolean)
+ if (words.length === 0 || !textToHighlight) return null
+
+ const pattern = new RegExp(`(${words.map(escapeRegExp).join('|')})`, 'gi')
+ return textToHighlight.split(pattern)
+ }, [searchWords, textToHighlight])
+
+ if (!parts) return <>{textToHighlight}>
+
+ // split içindeki tek sayılı indeksler yakalanan gruplardır.
+ return (
+ <>
+ {parts.map((part, index) =>
+ index % 2 === 1 ? (
+
+ {part}
+
+ ) : (
+ {part}
+ ),
+ )}
+ >
+ )
+}
+
+export default Highlighter
diff --git a/ui/src/components/shared/index.ts b/ui/src/components/shared/index.ts
index 826c7100..9fbfe043 100644
--- a/ui/src/components/shared/index.ts
+++ b/ui/src/components/shared/index.ts
@@ -3,7 +3,6 @@ export { default as AdaptableCard } from './AdaptableCard'
export { default as Affix } from './Affix'
export { default as AuthorityCheck } from './AuthorityCheck'
export { default as PermissionCheck } from './PermissionCheck'
-export { default as CalendarView } from './CalendarView'
export { default as ConfirmDialog } from './ConfirmDialog'
export { default as Container } from './Container'
export { default as DataTable } from './DataTable'
diff --git a/ui/src/components/template/HorizontalMenuContent/HorizontalMenuIcon.tsx b/ui/src/components/template/HorizontalMenuContent/HorizontalMenuIcon.tsx
index 4a2f3c94..c3266377 100644
--- a/ui/src/components/template/HorizontalMenuContent/HorizontalMenuIcon.tsx
+++ b/ui/src/components/template/HorizontalMenuContent/HorizontalMenuIcon.tsx
@@ -1,4 +1,4 @@
-import navigationIcon from '@/proxy/menus/navigation-icon.config'
+import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
import type { ElementType, ComponentPropsWithRef } from 'react'
import React from 'react'
import { FaQuestionCircle } from 'react-icons/fa'
@@ -18,6 +18,8 @@ export const Icon = ({
}
const HorizontalMenuIcon = ({ icon }: HorizontalMenuIconProps) => {
+ const navigationIcon = useNavigationIcons()
+
if (typeof icon !== 'string' || !icon) {
return <>>
}
diff --git a/ui/src/components/template/HorizontalMenuContent/HorizontalMenuItem.tsx b/ui/src/components/template/HorizontalMenuContent/HorizontalMenuItem.tsx
index c904abd9..bb8646b8 100644
--- a/ui/src/components/template/HorizontalMenuContent/HorizontalMenuItem.tsx
+++ b/ui/src/components/template/HorizontalMenuContent/HorizontalMenuItem.tsx
@@ -1,4 +1,4 @@
-import navigationIcon from '@/proxy/menus/navigation-icon.config'
+import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
import MenuItem from '@/components/ui/MenuItem'
import HorizontalMenuNavLink from './HorizontalMenuNavLink'
import type { NavMode } from '@/proxy/theme/models'
@@ -20,6 +20,7 @@ export type HorizontalMenuItemProps = {
const HorizontalMenuItem = ({ nav, isLink, manuVariant }: HorizontalMenuItemProps) => {
const { title, icon, path, isExternalLink } = nav
+ const navigationIcon = useNavigationIcons()
// Dinamik ikon render etmek için createElement kullanıyoruz
const renderIcon =
diff --git a/ui/src/components/template/Search.tsx b/ui/src/components/template/Search.tsx
index 70eaf12f..97e752a0 100644
--- a/ui/src/components/template/Search.tsx
+++ b/ui/src/components/template/Search.tsx
@@ -5,7 +5,7 @@ import withHeaderItem from '@/utils/hoc/withHeaderItem'
import useThemeClass from '@/utils/hooks/useThemeClass'
import classNames from 'classnames'
import { useEffect, useRef, useState } from 'react'
-import Highlighter from 'react-highlight-words'
+import Highlighter from '@/components/shared/Highlighter'
import { FaChevronRight, FaSearch } from 'react-icons/fa'
import { Link } from 'react-router-dom'
import { PermissionCheck } from '../shared'
@@ -61,7 +61,6 @@ const ListItem = (props: {
*/}
{
const { pathname } = useLocation()
+ const navigationIcon = useNavigationIcons()
const {
navMode,
diff --git a/ui/src/components/template/VerticalMenuContent/VerticalMenuIcon.tsx b/ui/src/components/template/VerticalMenuContent/VerticalMenuIcon.tsx
index 3e51b867..205d5f20 100644
--- a/ui/src/components/template/VerticalMenuContent/VerticalMenuIcon.tsx
+++ b/ui/src/components/template/VerticalMenuContent/VerticalMenuIcon.tsx
@@ -1,4 +1,4 @@
-import navigationIcon from '@/proxy/menus/navigation-icon.config'
+import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
import type { ElementType, ComponentPropsWithRef } from 'react'
import React from 'react'
@@ -17,6 +17,8 @@ export const Icon = ({
}
const VerticalMenuIcon = ({ icon }: VerticalMenuIconProps) => {
+ const navigationIcon = useNavigationIcons()
+
// Eğer icon boş veya string değilse, boş bir öğe döndür
if (typeof icon !== 'string' || !icon) {
return <>>
diff --git a/ui/src/components/ui/Alert/Alert.tsx b/ui/src/components/ui/Alert/Alert.tsx
index aada3d10..fe212d66 100644
--- a/ui/src/components/ui/Alert/Alert.tsx
+++ b/ui/src/components/ui/Alert/Alert.tsx
@@ -1,7 +1,7 @@
import { useState, forwardRef } from 'react'
import classNames from 'classnames'
import useTimeout from '../hooks/useTimeout'
-import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa'
+import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from '@/components/ui/icons'
import { motion } from 'framer-motion'
import CloseButton from '../CloseButton'
import StatusIcon from '../StatusIcon'
diff --git a/ui/src/components/ui/Checkbox/Group.tsx b/ui/src/components/ui/Checkbox/Group.tsx
index 6c4961bd..cbfd5f6b 100644
--- a/ui/src/components/ui/Checkbox/Group.tsx
+++ b/ui/src/components/ui/Checkbox/Group.tsx
@@ -1,8 +1,7 @@
import { forwardRef, useState, useCallback, useMemo, useEffect } from 'react'
import classNames from 'classnames'
import { CheckboxGroupContextProvider } from './context'
-import cloneDeep from 'lodash/cloneDeep'
-import remove from 'lodash/remove'
+import { cloneDeep, remove } from '@/utils/collection'
import shallowEqual from '../utils/shallowEqual'
import type { CommonProps } from '../@types/common'
import type { CheckboxGroupValue, CheckboxValue } from './context'
diff --git a/ui/src/components/ui/CloseButton/CloseButton.tsx b/ui/src/components/ui/CloseButton/CloseButton.tsx
index 1493344c..65d79ae1 100644
--- a/ui/src/components/ui/CloseButton/CloseButton.tsx
+++ b/ui/src/components/ui/CloseButton/CloseButton.tsx
@@ -1,5 +1,5 @@
import { forwardRef } from 'react'
-import { FaTimes } from 'react-icons/fa';
+import { FaTimes } from '@/components/ui/icons';
import classNames from 'classnames'
import type { CommonProps } from '../@types/common'
import type { MouseEvent } from 'react'
diff --git a/ui/src/components/ui/DatePicker/BasePicker.tsx b/ui/src/components/ui/DatePicker/BasePicker.tsx
index b4f74358..9a4551e9 100644
--- a/ui/src/components/ui/DatePicker/BasePicker.tsx
+++ b/ui/src/components/ui/DatePicker/BasePicker.tsx
@@ -3,7 +3,7 @@ import dayjs from 'dayjs'
import localizedFormat from 'dayjs/plugin/localizedFormat'
import { Input } from '../Input'
import useMergedRef from '../hooks/useMergeRef'
-import { FaCalendarAlt } from 'react-icons/fa';
+import { FaCalendarAlt } from '@/components/ui/icons';
import CloseButton from '../CloseButton'
import type { CommonProps, TypeAttributes } from '../@types/common'
import type {
diff --git a/ui/src/components/ui/DatePicker/tables/Header.tsx b/ui/src/components/ui/DatePicker/tables/Header.tsx
index 3657fdca..a93cee62 100644
--- a/ui/src/components/ui/DatePicker/tables/Header.tsx
+++ b/ui/src/components/ui/DatePicker/tables/Header.tsx
@@ -1,6 +1,6 @@
import classNames from 'classnames'
import { Button } from '../../Button'
-import { FaChevronRight, FaChevronLeft } from 'react-icons/fa';
+import { FaChevronRight, FaChevronLeft } from '@/components/ui/icons';
import type { CommonProps } from '../../@types/common'
export interface HeaderProps extends CommonProps {
diff --git a/ui/src/components/ui/Dialog/Dialog.tsx b/ui/src/components/ui/Dialog/Dialog.tsx
index dee3b953..51df263d 100644
--- a/ui/src/components/ui/Dialog/Dialog.tsx
+++ b/ui/src/components/ui/Dialog/Dialog.tsx
@@ -65,7 +65,7 @@ export interface DialogProps extends ReactModal.Props {
contentClassName?: string
draggable?: boolean
height?: string | number
- onClose?: (e: MouseEvent) => void
+ onClose?: (e: MouseEvent) => void
width?: string | number
showWindowControls?: boolean
onMaximize?: () => void
@@ -139,12 +139,12 @@ const Dialog = (props: DialogProps) => {
})
}, [currentSize.width, currentSize.height])
- const onCloseClick = (e: MouseEvent) => {
+ const onCloseClick = (e: MouseEvent) => {
onClose?.(e)
}
const handleMaximize = useCallback(
- (e: MouseEvent) => {
+ (e: MouseEvent) => {
e.stopPropagation()
if (!isMaximized) {
setIsMaximized(true)
@@ -156,7 +156,7 @@ const Dialog = (props: DialogProps) => {
)
const handleRestore = useCallback(
- (e: MouseEvent) => {
+ (e: MouseEvent) => {
e.stopPropagation()
if (isMaximized) {
setIsMaximized(false)
diff --git a/ui/src/components/ui/Dropdown/DropdownItem.tsx b/ui/src/components/ui/Dropdown/DropdownItem.tsx
index fa3e9240..c7cb7a84 100644
--- a/ui/src/components/ui/Dropdown/DropdownItem.tsx
+++ b/ui/src/components/ui/Dropdown/DropdownItem.tsx
@@ -7,7 +7,7 @@ import {
isValidElement,
cloneElement,
} from 'react'
-import isNil from 'lodash/isNil'
+import { isNil } from '@/utils/collection'
import chainedFunction from '../utils/chainedFunction'
import DropdownContext from './context/dropdownContext'
import MenuContext from './context/menuContext'
@@ -19,7 +19,7 @@ import DropdownMenuContext, {
DropdownMenuContextProvider,
} from './context/dropdownMenuContext'
import classNames from 'classnames'
-import { FaChevronRight, FaChevronLeft } from 'react-icons/fa';
+import { FaChevronRight, FaChevronLeft } from '@/components/ui/icons';
import MenuItem from '../MenuItem'
import { DROPDOWN_ITEM_TYPE } from '../utils/constants'
import type { CommonProps } from '../@types/common'
diff --git a/ui/src/components/ui/Dropdown/DropdownToggle.tsx b/ui/src/components/ui/Dropdown/DropdownToggle.tsx
index 638df914..2941e0ef 100644
--- a/ui/src/components/ui/Dropdown/DropdownToggle.tsx
+++ b/ui/src/components/ui/Dropdown/DropdownToggle.tsx
@@ -1,6 +1,6 @@
import { forwardRef } from 'react'
import classNames from 'classnames'
-import { FaChevronDown, FaChevronUp, FaChevronRight, FaChevronLeft } from 'react-icons/fa';
+import { FaChevronDown, FaChevronUp, FaChevronRight, FaChevronLeft } from '@/components/ui/icons';
import type { CommonProps } from '../@types/common'
import type { DropdownPlacement } from '../@types/placement'
import type { ReactNode } from 'react'
diff --git a/ui/src/components/ui/Dropdown/context/dropdownMenuContext.ts b/ui/src/components/ui/Dropdown/context/dropdownMenuContext.ts
index 3bcba4b2..42149996 100644
--- a/ui/src/components/ui/Dropdown/context/dropdownMenuContext.ts
+++ b/ui/src/components/ui/Dropdown/context/dropdownMenuContext.ts
@@ -1,5 +1,5 @@
import React, { createContext, useState, useRef, useCallback } from 'react'
-import isNil from 'lodash/isNil'
+import { isNil } from '@/utils/collection'
type Item = {
element: HTMLElement | null
diff --git a/ui/src/components/ui/Input/Input.tsx b/ui/src/components/ui/Input/Input.tsx
index 42c14df4..a3811c0d 100644
--- a/ui/src/components/ui/Input/Input.tsx
+++ b/ui/src/components/ui/Input/Input.tsx
@@ -1,7 +1,5 @@
import classNames from 'classnames'
-import get from 'lodash/get'
-import isEmpty from 'lodash/isEmpty'
-import isNil from 'lodash/isNil'
+import { get, isEmpty, isNil } from '@/utils/collection'
import type { ElementType, HTMLInputTypeAttribute, InputHTMLAttributes, ReactNode } from 'react'
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react'
import type { CommonProps, TypeAttributes } from '../@types/common'
@@ -75,7 +73,7 @@ const Input = forwardRef((props, ref
const { touched, errors } = form
const touchedField = get(touched, field.name)
const errorField = get(errors, field.name)
- validate = touchedField && errorField
+ validate = !!touchedField && !!errorField
}
if (typeof invalid === 'boolean') {
validate = invalid
diff --git a/ui/src/components/ui/Menu/MenuCollapse.tsx b/ui/src/components/ui/Menu/MenuCollapse.tsx
index dce32324..d5eac015 100644
--- a/ui/src/components/ui/Menu/MenuCollapse.tsx
+++ b/ui/src/components/ui/Menu/MenuCollapse.tsx
@@ -4,7 +4,7 @@ import { CollapseContextProvider } from './context/collapseContext'
import classNames from 'classnames'
import { motion } from 'framer-motion'
import MenuContext from './context/menuContext'
-import { FaChevronDown } from 'react-icons/fa';
+import { FaChevronDown } from '@/components/ui/icons';
import type { CommonProps } from '../@types/common'
import type { ReactNode, MouseEvent } from 'react'
diff --git a/ui/src/components/ui/Pagination/Next.tsx b/ui/src/components/ui/Pagination/Next.tsx
index 1eac2460..f280ed74 100644
--- a/ui/src/components/ui/Pagination/Next.tsx
+++ b/ui/src/components/ui/Pagination/Next.tsx
@@ -1,5 +1,5 @@
import classNames from 'classnames'
-import { FaChevronRight } from 'react-icons/fa';
+import { FaChevronRight } from '@/components/ui/icons';
import type { CommonProps } from '../@types/common'
import type { MouseEvent } from 'react'
diff --git a/ui/src/components/ui/Pagination/Pagers.tsx b/ui/src/components/ui/Pagination/Pagers.tsx
index 2818befc..f20f845d 100644
--- a/ui/src/components/ui/Pagination/Pagers.tsx
+++ b/ui/src/components/ui/Pagination/Pagers.tsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react'
import classNames from 'classnames'
-import { FaChevronCircleLeft, FaEllipsisH, FaChevronCircleRight } from 'react-icons/fa';
+import { FaChevronCircleLeft, FaEllipsisH, FaChevronCircleRight } from '@/components/ui/icons';
const PAGER_COUNT = 7
diff --git a/ui/src/components/ui/Pagination/Prev.tsx b/ui/src/components/ui/Pagination/Prev.tsx
index b04f112d..9d4edf5f 100644
--- a/ui/src/components/ui/Pagination/Prev.tsx
+++ b/ui/src/components/ui/Pagination/Prev.tsx
@@ -1,5 +1,5 @@
import classNames from 'classnames'
-import { FaChevronLeft } from 'react-icons/fa';
+import { FaChevronLeft } from '@/components/ui/icons';
import type { CommonProps } from '../@types/common'
import type { MouseEvent } from 'react'
diff --git a/ui/src/components/ui/Select/Select.tsx b/ui/src/components/ui/Select/Select.tsx
index 46bb8eca..f81e75e4 100644
--- a/ui/src/components/ui/Select/Select.tsx
+++ b/ui/src/components/ui/Select/Select.tsx
@@ -4,12 +4,11 @@ import ReactSelect, { components as ReactSelectComponents } from 'react-select'
import CreatableSelect from 'react-select/creatable'
import AsyncSelect from 'react-select/async'
import { TW_COLORS, BORDER_RADIUS } from '@/utils/tailwind'
-import isEmpty from 'lodash/isEmpty'
-import get from 'lodash/get'
+import { isEmpty, get } from '@/utils/collection'
import { useConfig } from '../ConfigProvider'
import { useForm } from '../Form/context'
import { useInputGroup } from '../InputGroup/context'
-import { FaCheck, FaChevronDown, FaTimes } from 'react-icons/fa'
+import { FaCheck, FaChevronDown, FaTimes } from '@/components/ui/icons'
import Spinner from '../Spinner/Spinner'
import type { CommonProps, TypeAttributes } from '../@types/common'
import type {
@@ -184,7 +183,7 @@ function SelectBase<
const touchedField = get(touched, field.name)
const errorField = get(errors, field.name)
- isInvalid = touchedField && errorField
+ isInvalid = !!touchedField && !!errorField
}
const getBoxShadow = (state: ControlProps