Ölü Paketler kaldırıldı
This commit is contained in:
parent
b517b7346e
commit
39ad5cab12
76 changed files with 1097 additions and 579 deletions
|
|
@ -12,7 +12,7 @@
|
||||||
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
|
"code": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||||
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
|
"nameKey": "App.SiteManagement.General.NewMemberNotificationEmails",
|
||||||
"descriptionKey": "App.GeneralNewMemberNotificationEmails.NewMemberNotificationEmailsDescription",
|
"descriptionKey": "App.GeneralNewMemberNotificationEmails.NewMemberNotificationEmailsDescription",
|
||||||
"defaultValue": "system@sozsoft.com",
|
"defaultValue": "destek@sozsoft.com",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "T|G|D",
|
"providers": "T|G|D",
|
||||||
"isInherited": false,
|
"isInherited": false,
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
"code": "App.SiteManagement.General.TimedLoginEmails",
|
"code": "App.SiteManagement.General.TimedLoginEmails",
|
||||||
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
|
"nameKey": "App.SiteManagement.General.TimedLoginEmails",
|
||||||
"descriptionKey": "App.GeneralTimedLoginEmails.TimedLoginEmailsDescription",
|
"descriptionKey": "App.GeneralTimedLoginEmails.TimedLoginEmailsDescription",
|
||||||
"defaultValue": "system@sozsoft.com",
|
"defaultValue": "destek@sozsoft.com",
|
||||||
"isVisibleToClients": false,
|
"isVisibleToClients": false,
|
||||||
"providers": "T|G|D",
|
"providers": "T|G|D",
|
||||||
"isInherited": false,
|
"isInherited": false,
|
||||||
|
|
|
||||||
|
|
@ -6846,6 +6846,12 @@
|
||||||
"tr": "Bültene Abone Ol",
|
"tr": "Bültene Abone Ol",
|
||||||
"en": "Subscribe to Newsletter"
|
"en": "Subscribe to Newsletter"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "App.PublicBlog.SearchPlaceholder",
|
||||||
|
"en": "Search blog posts...",
|
||||||
|
"tr": "Blog yazıları ara..."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "App.BlogSubscribe.SubscribeDesc",
|
"key": "App.BlogSubscribe.SubscribeDesc",
|
||||||
|
|
|
||||||
180
ui/package-lock.json
generated
180
ui/package-lock.json
generated
|
|
@ -11,14 +11,9 @@
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@floating-ui/react": "^0.27.20",
|
"@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",
|
"@marsidev/react-turnstile": "^1.6.0",
|
||||||
"@microsoft/signalr": "^10.0.11",
|
"@microsoft/signalr": "^10.0.11",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@tanstack/react-query": "^5.101.4",
|
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.19.0",
|
"axios": "^1.19.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
@ -33,30 +28,23 @@
|
||||||
"formik": "^2.4.9",
|
"formik": "^2.4.9",
|
||||||
"framer-motion": "^13.1.0",
|
"framer-motion": "^13.1.0",
|
||||||
"jspdf": "^4.2.1",
|
"jspdf": "^4.2.1",
|
||||||
"jwt-decode": "^4.0.0",
|
|
||||||
"lodash": "^4.18.1",
|
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.8",
|
||||||
"react-advanced-cropper": "^0.20.1",
|
"react-advanced-cropper": "^0.20.1",
|
||||||
"react-arborist": "^3.16.0",
|
"react-arborist": "^3.16.0",
|
||||||
"react-dom": "^19.2.8",
|
"react-dom": "^19.2.8",
|
||||||
"react-error-boundary": "^6.1.2",
|
|
||||||
"react-highlight-words": "^0.21.0",
|
|
||||||
"react-icons": "^5.7.0",
|
"react-icons": "^5.7.0",
|
||||||
"react-modal": "^3.16.3",
|
"react-modal": "^3.16.3",
|
||||||
"react-router-dom": "^7.18.2",
|
"react-router-dom": "^7.18.2",
|
||||||
"react-select": "^5.10.2",
|
"react-select": "^5.10.2",
|
||||||
"yet-another-react-lightbox": "^3.32.2",
|
|
||||||
"yup": "^1.7.1"
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.5",
|
"@eslint/js": "^9.39.5",
|
||||||
"@tailwindcss/typography": "^0.5.20",
|
"@tailwindcss/typography": "^0.5.20",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/lodash": "^4.17.25",
|
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.18",
|
"@types/react": "^19.2.18",
|
||||||
"@types/react-dom": "^19.2.4",
|
"@types/react-dom": "^19.2.4",
|
||||||
"@types/react-highlight-words": "^0.20.1",
|
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
||||||
"@typescript-eslint/parser": "^8.67.0",
|
"@typescript-eslint/parser": "^8.67.0",
|
||||||
|
|
@ -2182,57 +2170,6 @@
|
||||||
"integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
|
"integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.2",
|
"version": "0.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||||
|
|
@ -3266,32 +3203,6 @@
|
||||||
"tailwindcss": ">=3.0.0 || >=4.0.0 || insiders"
|
"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": {
|
"node_modules/@tanstack/react-table": {
|
||||||
"version": "8.21.3",
|
"version": "8.21.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||||
|
|
@ -3402,13 +3313,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.13.3",
|
"version": "24.13.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
|
||||||
|
|
@ -3463,16 +3367,6 @@
|
||||||
"@types/react": "^19.2.0"
|
"@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": {
|
"node_modules/@types/react-modal": {
|
||||||
"version": "3.16.3",
|
"version": "3.16.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz",
|
||||||
|
|
@ -7669,12 +7563,6 @@
|
||||||
"hermes-estree": "0.25.1"
|
"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": {
|
"node_modules/hoist-non-react-statics": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
"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"
|
"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": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
|
|
@ -10383,17 +10262,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
|
|
@ -10647,34 +10515,12 @@
|
||||||
"react": "^19.2.8"
|
"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": {
|
"node_modules/react-fast-compare": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||||
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
|
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/react-icons": {
|
||||||
"version": "5.7.0",
|
"version": "5.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz",
|
||||||
|
|
@ -13240,32 +13086,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,9 @@
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@floating-ui/react": "^0.27.20",
|
"@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",
|
"@marsidev/react-turnstile": "^1.6.0",
|
||||||
"@microsoft/signalr": "^10.0.11",
|
"@microsoft/signalr": "^10.0.11",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@tanstack/react-query": "^5.101.4",
|
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"axios": "^1.19.0",
|
"axios": "^1.19.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
|
|
@ -51,30 +46,23 @@
|
||||||
"formik": "^2.4.9",
|
"formik": "^2.4.9",
|
||||||
"framer-motion": "^13.1.0",
|
"framer-motion": "^13.1.0",
|
||||||
"jspdf": "^4.2.1",
|
"jspdf": "^4.2.1",
|
||||||
"jwt-decode": "^4.0.0",
|
|
||||||
"lodash": "^4.18.1",
|
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.8",
|
||||||
"react-advanced-cropper": "^0.20.1",
|
"react-advanced-cropper": "^0.20.1",
|
||||||
"react-arborist": "^3.16.0",
|
"react-arborist": "^3.16.0",
|
||||||
"react-dom": "^19.2.8",
|
"react-dom": "^19.2.8",
|
||||||
"react-error-boundary": "^6.1.2",
|
|
||||||
"react-highlight-words": "^0.21.0",
|
|
||||||
"react-icons": "^5.7.0",
|
"react-icons": "^5.7.0",
|
||||||
"react-modal": "^3.16.3",
|
"react-modal": "^3.16.3",
|
||||||
"react-router-dom": "^7.18.2",
|
"react-router-dom": "^7.18.2",
|
||||||
"react-select": "^5.10.2",
|
"react-select": "^5.10.2",
|
||||||
"yet-another-react-lightbox": "^3.32.2",
|
|
||||||
"yup": "^1.7.1"
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.5",
|
"@eslint/js": "^9.39.5",
|
||||||
"@tailwindcss/typography": "^0.5.20",
|
"@tailwindcss/typography": "^0.5.20",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/lodash": "^4.17.25",
|
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.18",
|
"@types/react": "^19.2.18",
|
||||||
"@types/react-dom": "^19.2.4",
|
"@types/react-dom": "^19.2.4",
|
||||||
"@types/react-highlight-words": "^0.20.1",
|
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
||||||
"@typescript-eslint/parser": "^8.67.0",
|
"@typescript-eslint/parser": "^8.67.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import Layout from '@/components/layouts'
|
import Layout from '@/components/layouts'
|
||||||
import Theme from '@/components/template/Theme'
|
import Theme from '@/components/template/Theme'
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
||||||
import { StoreProvider } from 'easy-peasy'
|
import { StoreProvider } from 'easy-peasy'
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
import { store } from './store'
|
import { store } from './store'
|
||||||
|
|
@ -10,15 +9,6 @@ import { registerServiceWorker } from './views/version/swRegistration'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import DevExtremeBoundary from './components/devExtreme/DevExtremeBoundary'
|
import DevExtremeBoundary from './components/devExtreme/DevExtremeBoundary'
|
||||||
|
|
||||||
const queryClient = new QueryClient({
|
|
||||||
defaultOptions: {
|
|
||||||
queries: {
|
|
||||||
staleTime: 30_000,
|
|
||||||
refetchOnWindowFocus: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
registerServiceWorker()
|
registerServiceWorker()
|
||||||
|
|
@ -30,13 +20,11 @@ function App() {
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<DynamicRoutesProvider>
|
<DynamicRoutesProvider>
|
||||||
<ComponentProvider>
|
<ComponentProvider>
|
||||||
<QueryClientProvider client={queryClient}>
|
<DevExtremeBoundary>
|
||||||
<DevExtremeBoundary>
|
<Theme>
|
||||||
<Theme>
|
<Layout />
|
||||||
<Layout />
|
</Theme>
|
||||||
</Theme>
|
</DevExtremeBoundary>
|
||||||
</DevExtremeBoundary>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</ComponentProvider>
|
</ComponentProvider>
|
||||||
</DynamicRoutesProvider>
|
</DynamicRoutesProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
|
||||||
<div className={classNames('calendar', wrapperClass)}>
|
|
||||||
<FullCalendar
|
|
||||||
initialView="dayGridMonth"
|
|
||||||
headerToolbar={{
|
|
||||||
left: 'title',
|
|
||||||
center: '',
|
|
||||||
right: 'dayGridMonth,timeGridWeek,timeGridDay prev,next',
|
|
||||||
}}
|
|
||||||
eventContent={(arg) => {
|
|
||||||
const { extendedProps } = arg.event
|
|
||||||
const { isEnd, isStart } = arg
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
'custom-calendar-event',
|
|
||||||
extendedProps.eventColor
|
|
||||||
? (eventColors(defaultColorList) ||
|
|
||||||
defaultColorList)[
|
|
||||||
extendedProps.eventColor
|
|
||||||
]?.bg
|
|
||||||
: '',
|
|
||||||
extendedProps.eventColor
|
|
||||||
? (eventColors(defaultColorList) ||
|
|
||||||
defaultColorList)[
|
|
||||||
extendedProps.eventColor
|
|
||||||
]?.text
|
|
||||||
: '',
|
|
||||||
isEnd &&
|
|
||||||
!isStart &&
|
|
||||||
'!rounded-tl-none !rounded-bl-none !rtl:rounded-tr-none !rtl:rounded-br-none',
|
|
||||||
!isEnd &&
|
|
||||||
isStart &&
|
|
||||||
'!rounded-tr-none !rounded-br-none !rtl:rounded-tl-none !rtl:rounded-bl-none'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!(isEnd && !isStart) && (
|
|
||||||
<Badge
|
|
||||||
className={classNames(
|
|
||||||
'mr-1 rtl:ml-1',
|
|
||||||
extendedProps.eventColor
|
|
||||||
? (eventColors(defaultColorList) ||
|
|
||||||
defaultColorList)[
|
|
||||||
extendedProps.eventColor
|
|
||||||
].dot
|
|
||||||
: ''
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!(isEnd && !isStart) && (
|
|
||||||
<span>{arg.timeText}</span>
|
|
||||||
)}
|
|
||||||
<span className="font-semibold ml-1 rtl:mr-1">
|
|
||||||
{arg.event.title}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CalendarView
|
|
||||||
62
ui/src/components/shared/ErrorBoundary.tsx
Normal file
62
ui/src/components/shared/ErrorBoundary.tsx
Normal file
|
|
@ -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<FallbackProps>
|
||||||
|
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<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||||
|
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 <FallbackComponent error={this.state.error} resetErrorBoundary={this.reset} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorBoundary
|
||||||
44
ui/src/components/shared/Highlighter.tsx
Normal file
44
ui/src/components/shared/Highlighter.tsx
Normal file
|
|
@ -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 <mark> 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 ? (
|
||||||
|
<mark key={index} className={highlightClassName}>
|
||||||
|
{part}
|
||||||
|
</mark>
|
||||||
|
) : (
|
||||||
|
<Fragment key={index}>{part}</Fragment>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Highlighter
|
||||||
|
|
@ -3,7 +3,6 @@ export { default as AdaptableCard } from './AdaptableCard'
|
||||||
export { default as Affix } from './Affix'
|
export { default as Affix } from './Affix'
|
||||||
export { default as AuthorityCheck } from './AuthorityCheck'
|
export { default as AuthorityCheck } from './AuthorityCheck'
|
||||||
export { default as PermissionCheck } from './PermissionCheck'
|
export { default as PermissionCheck } from './PermissionCheck'
|
||||||
export { default as CalendarView } from './CalendarView'
|
|
||||||
export { default as ConfirmDialog } from './ConfirmDialog'
|
export { default as ConfirmDialog } from './ConfirmDialog'
|
||||||
export { default as Container } from './Container'
|
export { default as Container } from './Container'
|
||||||
export { default as DataTable } from './DataTable'
|
export { default as DataTable } from './DataTable'
|
||||||
|
|
|
||||||
|
|
@ -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 type { ElementType, ComponentPropsWithRef } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FaQuestionCircle } from 'react-icons/fa'
|
import { FaQuestionCircle } from 'react-icons/fa'
|
||||||
|
|
@ -18,6 +18,8 @@ export const Icon = <T extends ElementType>({
|
||||||
}
|
}
|
||||||
|
|
||||||
const HorizontalMenuIcon = ({ icon }: HorizontalMenuIconProps) => {
|
const HorizontalMenuIcon = ({ icon }: HorizontalMenuIconProps) => {
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
if (typeof icon !== 'string' || !icon) {
|
if (typeof icon !== 'string' || !icon) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 MenuItem from '@/components/ui/MenuItem'
|
||||||
import HorizontalMenuNavLink from './HorizontalMenuNavLink'
|
import HorizontalMenuNavLink from './HorizontalMenuNavLink'
|
||||||
import type { NavMode } from '@/proxy/theme/models'
|
import type { NavMode } from '@/proxy/theme/models'
|
||||||
|
|
@ -20,6 +20,7 @@ export type HorizontalMenuItemProps = {
|
||||||
|
|
||||||
const HorizontalMenuItem = ({ nav, isLink, manuVariant }: HorizontalMenuItemProps) => {
|
const HorizontalMenuItem = ({ nav, isLink, manuVariant }: HorizontalMenuItemProps) => {
|
||||||
const { title, icon, path, isExternalLink } = nav
|
const { title, icon, path, isExternalLink } = nav
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
// Dinamik ikon render etmek için createElement kullanıyoruz
|
// Dinamik ikon render etmek için createElement kullanıyoruz
|
||||||
const renderIcon =
|
const renderIcon =
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import withHeaderItem from '@/utils/hoc/withHeaderItem'
|
||||||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
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 { FaChevronRight, FaSearch } from 'react-icons/fa'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { PermissionCheck } from '../shared'
|
import { PermissionCheck } from '../shared'
|
||||||
|
|
@ -61,7 +61,6 @@ const ListItem = (props: {
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="text-gray-900 dark:text-gray-300">
|
<div className="text-gray-900 dark:text-gray-300">
|
||||||
<Highlighter
|
<Highlighter
|
||||||
autoEscape
|
|
||||||
highlightClassName={classNames(
|
highlightClassName={classNames(
|
||||||
textTheme,
|
textTheme,
|
||||||
'underline bg-transparent font-semibold dark:text-white',
|
'underline bg-transparent font-semibold dark:text-white',
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import StackedSideNavMini, { SelectedMenuItem } from './StackedSideNavMini'
|
import StackedSideNavMini, { SelectedMenuItem } from './StackedSideNavMini'
|
||||||
import StackedSideNavSecondary from './StackedSideNavSecondary'
|
import StackedSideNavSecondary from './StackedSideNavSecondary'
|
||||||
import useResponsive from '@/utils/hooks/useResponsive'
|
import useResponsive from '@/utils/hooks/useResponsive'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
|
|
||||||
const stackedSideNavDefaultStyle = {
|
const stackedSideNavDefaultStyle = {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { PermissionCheck } from '@/components/shared'
|
||||||
import Logo from '@/components/template/Logo'
|
import Logo from '@/components/template/Logo'
|
||||||
import Menu from '@/components/ui/Menu'
|
import Menu from '@/components/ui/Menu'
|
||||||
import ScrollBar from '@/components/ui/ScrollBar'
|
import ScrollBar from '@/components/ui/ScrollBar'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { NAV_ITEM_TYPE_ITEM } from '@/constants/navigation.constant'
|
import { NAV_ITEM_TYPE_ITEM } from '@/constants/navigation.constant'
|
||||||
import {
|
import {
|
||||||
NAV_MODE_DARK,
|
NAV_MODE_DARK,
|
||||||
|
|
@ -15,7 +15,7 @@ import {
|
||||||
} from '@/constants/theme.constant'
|
} from '@/constants/theme.constant'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import useMenuActive from '@/utils/hooks/useMenuActive'
|
import useMenuActive from '@/utils/hooks/useMenuActive'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { FaQuestionCircle } from 'react-icons/fa'
|
import { FaQuestionCircle } from 'react-icons/fa'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
|
|
@ -41,6 +41,7 @@ interface StackedSideNavMiniProps extends CommonProps {
|
||||||
|
|
||||||
const StackedSideNavMini = (props: StackedSideNavMiniProps) => {
|
const StackedSideNavMini = (props: StackedSideNavMiniProps) => {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
navMode,
|
navMode,
|
||||||
|
|
|
||||||
|
|
@ -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 type { ElementType, ComponentPropsWithRef } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
|
@ -17,6 +17,8 @@ export const Icon = <T extends ElementType>({
|
||||||
}
|
}
|
||||||
|
|
||||||
const VerticalMenuIcon = ({ icon }: VerticalMenuIconProps) => {
|
const VerticalMenuIcon = ({ icon }: VerticalMenuIconProps) => {
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
// Eğer icon boş veya string değilse, boş bir öğe döndür
|
// Eğer icon boş veya string değilse, boş bir öğe döndür
|
||||||
if (typeof icon !== 'string' || !icon) {
|
if (typeof icon !== 'string' || !icon) {
|
||||||
return <></>
|
return <></>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, forwardRef } from 'react'
|
import { useState, forwardRef } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import useTimeout from '../hooks/useTimeout'
|
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 { motion } from 'framer-motion'
|
||||||
import CloseButton from '../CloseButton'
|
import CloseButton from '../CloseButton'
|
||||||
import StatusIcon from '../StatusIcon'
|
import StatusIcon from '../StatusIcon'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { forwardRef, useState, useCallback, useMemo, useEffect } from 'react'
|
import { forwardRef, useState, useCallback, useMemo, useEffect } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { CheckboxGroupContextProvider } from './context'
|
import { CheckboxGroupContextProvider } from './context'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import { cloneDeep, remove } from '@/utils/collection'
|
||||||
import remove from 'lodash/remove'
|
|
||||||
import shallowEqual from '../utils/shallowEqual'
|
import shallowEqual from '../utils/shallowEqual'
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { CheckboxGroupValue, CheckboxValue } from './context'
|
import type { CheckboxGroupValue, CheckboxValue } from './context'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
import { FaTimes } from 'react-icons/fa';
|
import { FaTimes } from '@/components/ui/icons';
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import dayjs from 'dayjs'
|
||||||
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||||
import { Input } from '../Input'
|
import { Input } from '../Input'
|
||||||
import useMergedRef from '../hooks/useMergeRef'
|
import useMergedRef from '../hooks/useMergeRef'
|
||||||
import { FaCalendarAlt } from 'react-icons/fa';
|
import { FaCalendarAlt } from '@/components/ui/icons';
|
||||||
import CloseButton from '../CloseButton'
|
import CloseButton from '../CloseButton'
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { Button } from '../../Button'
|
import { Button } from '../../Button'
|
||||||
import { FaChevronRight, FaChevronLeft } from 'react-icons/fa';
|
import { FaChevronRight, FaChevronLeft } from '@/components/ui/icons';
|
||||||
import type { CommonProps } from '../../@types/common'
|
import type { CommonProps } from '../../@types/common'
|
||||||
|
|
||||||
export interface HeaderProps extends CommonProps {
|
export interface HeaderProps extends CommonProps {
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export interface DialogProps extends ReactModal.Props {
|
||||||
contentClassName?: string
|
contentClassName?: string
|
||||||
draggable?: boolean
|
draggable?: boolean
|
||||||
height?: string | number
|
height?: string | number
|
||||||
onClose?: (e: MouseEvent<HTMLSpanElement>) => void
|
onClose?: (e: MouseEvent<HTMLElement>) => void
|
||||||
width?: string | number
|
width?: string | number
|
||||||
showWindowControls?: boolean
|
showWindowControls?: boolean
|
||||||
onMaximize?: () => void
|
onMaximize?: () => void
|
||||||
|
|
@ -139,12 +139,12 @@ const Dialog = (props: DialogProps) => {
|
||||||
})
|
})
|
||||||
}, [currentSize.width, currentSize.height])
|
}, [currentSize.width, currentSize.height])
|
||||||
|
|
||||||
const onCloseClick = (e: MouseEvent<HTMLSpanElement>) => {
|
const onCloseClick = (e: MouseEvent<HTMLElement>) => {
|
||||||
onClose?.(e)
|
onClose?.(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMaximize = useCallback(
|
const handleMaximize = useCallback(
|
||||||
(e: MouseEvent<HTMLSpanElement>) => {
|
(e: MouseEvent<HTMLElement>) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (!isMaximized) {
|
if (!isMaximized) {
|
||||||
setIsMaximized(true)
|
setIsMaximized(true)
|
||||||
|
|
@ -156,7 +156,7 @@ const Dialog = (props: DialogProps) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleRestore = useCallback(
|
const handleRestore = useCallback(
|
||||||
(e: MouseEvent<HTMLSpanElement>) => {
|
(e: MouseEvent<HTMLElement>) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (isMaximized) {
|
if (isMaximized) {
|
||||||
setIsMaximized(false)
|
setIsMaximized(false)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
isValidElement,
|
isValidElement,
|
||||||
cloneElement,
|
cloneElement,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import isNil from 'lodash/isNil'
|
import { isNil } from '@/utils/collection'
|
||||||
import chainedFunction from '../utils/chainedFunction'
|
import chainedFunction from '../utils/chainedFunction'
|
||||||
import DropdownContext from './context/dropdownContext'
|
import DropdownContext from './context/dropdownContext'
|
||||||
import MenuContext from './context/menuContext'
|
import MenuContext from './context/menuContext'
|
||||||
|
|
@ -19,7 +19,7 @@ import DropdownMenuContext, {
|
||||||
DropdownMenuContextProvider,
|
DropdownMenuContextProvider,
|
||||||
} from './context/dropdownMenuContext'
|
} from './context/dropdownMenuContext'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FaChevronRight, FaChevronLeft } from 'react-icons/fa';
|
import { FaChevronRight, FaChevronLeft } from '@/components/ui/icons';
|
||||||
import MenuItem from '../MenuItem'
|
import MenuItem from '../MenuItem'
|
||||||
import { DROPDOWN_ITEM_TYPE } from '../utils/constants'
|
import { DROPDOWN_ITEM_TYPE } from '../utils/constants'
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
import classNames from 'classnames'
|
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 { CommonProps } from '../@types/common'
|
||||||
import type { DropdownPlacement } from '../@types/placement'
|
import type { DropdownPlacement } from '../@types/placement'
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { createContext, useState, useRef, useCallback } from 'react'
|
import React, { createContext, useState, useRef, useCallback } from 'react'
|
||||||
import isNil from 'lodash/isNil'
|
import { isNil } from '@/utils/collection'
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
element: HTMLElement | null
|
element: HTMLElement | null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import get from 'lodash/get'
|
import { get, isEmpty, isNil } from '@/utils/collection'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
|
||||||
import isNil from 'lodash/isNil'
|
|
||||||
import type { ElementType, HTMLInputTypeAttribute, InputHTMLAttributes, ReactNode } from 'react'
|
import type { ElementType, HTMLInputTypeAttribute, InputHTMLAttributes, ReactNode } from 'react'
|
||||||
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react'
|
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
|
|
@ -75,7 +73,7 @@ const Input = forwardRef<ElementType | HTMLInputElement, InputProps>((props, ref
|
||||||
const { touched, errors } = form
|
const { touched, errors } = form
|
||||||
const touchedField = get(touched, field.name)
|
const touchedField = get(touched, field.name)
|
||||||
const errorField = get(errors, field.name)
|
const errorField = get(errors, field.name)
|
||||||
validate = touchedField && errorField
|
validate = !!touchedField && !!errorField
|
||||||
}
|
}
|
||||||
if (typeof invalid === 'boolean') {
|
if (typeof invalid === 'boolean') {
|
||||||
validate = invalid
|
validate = invalid
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { CollapseContextProvider } from './context/collapseContext'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import MenuContext from './context/menuContext'
|
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 { CommonProps } from '../@types/common'
|
||||||
import type { ReactNode, MouseEvent } from 'react'
|
import type { ReactNode, MouseEvent } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FaChevronRight } from 'react-icons/fa';
|
import { FaChevronRight } from '@/components/ui/icons';
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react'
|
import React, { useState, useEffect, useCallback, useMemo } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FaChevronCircleLeft, FaEllipsisH, FaChevronCircleRight } from 'react-icons/fa';
|
import { FaChevronCircleLeft, FaEllipsisH, FaChevronCircleRight } from '@/components/ui/icons';
|
||||||
|
|
||||||
const PAGER_COUNT = 7
|
const PAGER_COUNT = 7
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FaChevronLeft } from 'react-icons/fa';
|
import { FaChevronLeft } from '@/components/ui/icons';
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ import ReactSelect, { components as ReactSelectComponents } from 'react-select'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import AsyncSelect from 'react-select/async'
|
import AsyncSelect from 'react-select/async'
|
||||||
import { TW_COLORS, BORDER_RADIUS } from '@/utils/tailwind'
|
import { TW_COLORS, BORDER_RADIUS } from '@/utils/tailwind'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty, get } from '@/utils/collection'
|
||||||
import get from 'lodash/get'
|
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import { useForm } from '../Form/context'
|
import { useForm } from '../Form/context'
|
||||||
import { useInputGroup } from '../InputGroup/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 Spinner from '../Spinner/Spinner'
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -184,7 +183,7 @@ function SelectBase<
|
||||||
const touchedField = get(touched, field.name)
|
const touchedField = get(touched, field.name)
|
||||||
const errorField = get(errors, field.name)
|
const errorField = get(errors, field.name)
|
||||||
|
|
||||||
isInvalid = touchedField && errorField
|
isInvalid = !!touchedField && !!errorField
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBoxShadow = (state: ControlProps<Option, IsMulti, Group>) => {
|
const getBoxShadow = (state: ControlProps<Option, IsMulti, Group>) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import { FaSpinner } from 'react-icons/fa';
|
import { FaSpinner } from '@/components/ui/icons';
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { ElementType } from 'react'
|
import type { ElementType } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa';
|
import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from '@/components/ui/icons';
|
||||||
import type { TypeAttributes, CommonProps } from '../@types/common'
|
import type { TypeAttributes, CommonProps } from '../@types/common'
|
||||||
import type { ReactElement, ReactNode } from 'react'
|
import type { ReactElement, ReactNode } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import { FaCheck, FaTimes } from 'react-icons/fa';
|
import { FaCheck, FaTimes } from '@/components/ui/icons';
|
||||||
import { STEPS_STATUS } from '../utils/constants'
|
import { STEPS_STATUS } from '../utils/constants'
|
||||||
import type { CommonProps, StepStatus } from '../@types/common'
|
import type { CommonProps, StepStatus } from '../@types/common'
|
||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import { FaSort, FaSortUp, FaSortDown } from 'react-icons/fa'
|
import { FaSort, FaSortUp, FaSortDown } from '@/components/ui/icons'
|
||||||
|
|
||||||
export type SorterProps = { sort?: boolean | 'asc' | 'desc' }
|
export type SorterProps = { sort?: boolean | 'asc' | 'desc' }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
createAmPmHandler,
|
createAmPmHandler,
|
||||||
createTimeHandler,
|
createTimeHandler,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import { FaClock } from 'react-icons/fa';
|
import { FaClock } from '@/components/ui/icons';
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
import type { ReactNode, RefObject, Ref } from 'react'
|
import type { ReactNode, RefObject, Ref } from 'react'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import useMergedRef from '../hooks/useMergeRef'
|
||||||
import useDidUpdate from '../hooks/useDidUpdate'
|
import useDidUpdate from '../hooks/useDidUpdate'
|
||||||
import TimeInput from './TimeInput'
|
import TimeInput from './TimeInput'
|
||||||
import CloseButton from '../CloseButton'
|
import CloseButton from '../CloseButton'
|
||||||
import { FaClock } from 'react-icons/fa';
|
import { FaClock } from '@/components/ui/icons';
|
||||||
import Input from '../Input/Input'
|
import Input from '../Input/Input'
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
import type { RefObject, Ref, ReactNode } from 'react'
|
import type { RefObject, Ref, ReactNode } from 'react'
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
FaCaretLeft,
|
FaCaretLeft,
|
||||||
FaCaretUp,
|
FaCaretUp,
|
||||||
FaCaretRight,
|
FaCaretRight,
|
||||||
} from 'react-icons/fa';
|
} from '@/components/ui/icons';
|
||||||
|
|
||||||
export type ArrowPlacement =
|
export type ArrowPlacement =
|
||||||
| 'top'
|
| 'top'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FaFilePdf, FaFileArchive, FaFile } from 'react-icons/fa';
|
import { FaFilePdf, FaFileArchive, FaFile } from '@/components/ui/icons';
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
|
|
||||||
const BYTE = 1000
|
const BYTE = 1000
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { forwardRef, useRef, useState, useCallback, useEffect } from 'react'
|
import { forwardRef, useRef, useState, useCallback, useEffect } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import { cloneDeep } from '@/utils/collection'
|
||||||
import FileItem from './FileItem'
|
import FileItem from './FileItem'
|
||||||
import Button from '../Button/Button'
|
import Button from '../Button/Button'
|
||||||
import CloseButton from '../CloseButton'
|
import CloseButton from '../CloseButton'
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,87 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef } from 'react'
|
||||||
import { FaTimes, FaWindowMaximize, FaWindowRestore } from 'react-icons/fa'
|
import { FaTimes, FaWindowMaximize, FaWindowRestore } from '@/components/ui/icons'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import type { CommonProps } from '../@types/common'
|
import type { CommonProps } from '../@types/common'
|
||||||
import type { MouseEvent } from 'react'
|
import type { MouseEvent } from 'react'
|
||||||
|
|
||||||
export interface WindowControlsProps extends CommonProps {
|
export interface WindowControlsProps extends CommonProps {
|
||||||
absolute?: boolean
|
absolute?: boolean
|
||||||
defaultStyle?: boolean
|
|
||||||
isMaximized?: boolean
|
isMaximized?: boolean
|
||||||
onClose?: (e: MouseEvent<HTMLSpanElement>) => void
|
onClose?: (e: MouseEvent<HTMLElement>) => void
|
||||||
onMaximize?: (e: MouseEvent<HTMLSpanElement>) => void
|
onMaximize?: (e: MouseEvent<HTMLElement>) => void
|
||||||
onRestore?: (e: MouseEvent<HTMLSpanElement>) => void
|
onRestore?: (e: MouseEvent<HTMLElement>) => void
|
||||||
showMaximize?: boolean
|
showMaximize?: boolean
|
||||||
showRestore?: boolean
|
|
||||||
showClose?: boolean
|
showClose?: boolean
|
||||||
|
maximizeTitle?: string
|
||||||
|
restoreTitle?: string
|
||||||
|
closeTitle?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buttonClass = 'window-controls-btn'
|
||||||
|
|
||||||
const WindowControls = forwardRef<HTMLDivElement, WindowControlsProps>((props, ref) => {
|
const WindowControls = forwardRef<HTMLDivElement, WindowControlsProps>((props, ref) => {
|
||||||
const {
|
const {
|
||||||
absolute,
|
absolute,
|
||||||
className,
|
className,
|
||||||
defaultStyle,
|
|
||||||
isMaximized,
|
isMaximized,
|
||||||
onClose,
|
onClose,
|
||||||
onMaximize,
|
onMaximize,
|
||||||
onRestore,
|
onRestore,
|
||||||
showMaximize = true,
|
showMaximize = true,
|
||||||
showRestore = true,
|
|
||||||
showClose = true,
|
showClose = true,
|
||||||
|
maximizeTitle = 'Maximize',
|
||||||
|
restoreTitle = 'Restore',
|
||||||
|
closeTitle = 'Close',
|
||||||
...rest
|
...rest
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const windowControlsAbsoluteClass = 'absolute z-10'
|
|
||||||
|
|
||||||
const windowControlsClass = classNames(
|
const windowControlsClass = classNames(
|
||||||
'window-controls flex items-center gap-2',
|
'window-controls flex items-center gap-2',
|
||||||
defaultStyle && 'window-controls-default',
|
absolute && 'absolute z-10 top-3',
|
||||||
absolute && windowControlsAbsoluteClass,
|
|
||||||
className
|
className
|
||||||
)
|
)
|
||||||
|
|
||||||
const buttonClass = 'close-btn cursor-pointer hover:opacity-70 transition-opacity'
|
const stopPropagation = (e: MouseEvent<HTMLButtonElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={windowControlsClass} {...rest} ref={ref}>
|
<div className={windowControlsClass} {...rest} ref={ref}>
|
||||||
{showMaximize && !isMaximized && (
|
{showMaximize &&
|
||||||
<span className={buttonClass} role="button" onClick={onMaximize} title="Maximize">
|
(isMaximized ? (
|
||||||
<FaWindowMaximize />
|
<button
|
||||||
</span>
|
type="button"
|
||||||
)}
|
className={buttonClass}
|
||||||
{showRestore && isMaximized && (
|
title={restoreTitle}
|
||||||
<span className={buttonClass} role="button" onClick={onRestore} title="Restore">
|
aria-label={restoreTitle}
|
||||||
<FaWindowRestore />
|
onMouseDown={stopPropagation}
|
||||||
</span>
|
onClick={onRestore}
|
||||||
)}
|
>
|
||||||
|
<FaWindowRestore />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={buttonClass}
|
||||||
|
title={maximizeTitle}
|
||||||
|
aria-label={maximizeTitle}
|
||||||
|
onMouseDown={stopPropagation}
|
||||||
|
onClick={onMaximize}
|
||||||
|
>
|
||||||
|
<FaWindowMaximize />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
{showClose && (
|
{showClose && (
|
||||||
<span className={buttonClass} role="button" onClick={onClose} title="Close">
|
<button
|
||||||
|
type="button"
|
||||||
|
className={classNames(buttonClass, 'window-controls-close')}
|
||||||
|
title={closeTitle}
|
||||||
|
aria-label={closeTitle}
|
||||||
|
onMouseDown={stopPropagation}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
<FaTimes />
|
<FaTimes />
|
||||||
</span>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import uniqueId from 'lodash/uniqueId'
|
import { uniqueId } from '@/utils/collection'
|
||||||
import createUID from '../utils/createUid'
|
import createUID from '../utils/createUid'
|
||||||
|
|
||||||
export default function useUniqueId(prefix = '', len = 10) {
|
export default function useUniqueId(prefix = '', len = 10) {
|
||||||
|
|
|
||||||
412
ui/src/components/ui/icons.tsx
Normal file
412
ui/src/components/ui/icons.tsx
Normal file
|
|
@ -0,0 +1,412 @@
|
||||||
|
import { GenIcon } from 'react-icons/lib'
|
||||||
|
import type { IconType } from 'react-icons/lib'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform kabuğunun (ui/ bileşenleri) kullandığı Font Awesome ikonları.
|
||||||
|
*
|
||||||
|
* NEDEN BURADA: menü ikonları veritabanından ada göre çözüldüğü için
|
||||||
|
* `navigation-icon.config` tüm FA setini namespace olarak yüklemek zorunda.
|
||||||
|
* Bu, `react-icons/fa` modülünü tree-shake edilemez hale getiriyor; sonuçta
|
||||||
|
* TEK bir ikon import eden dosya bile ~1.28 MB'lık fa chunk'ını sürüklüyordu.
|
||||||
|
* ui/ bileşenleri uygulamanın her yerinde (ve ilk boyamada) kullanıldığından,
|
||||||
|
* kullandıkları 24 ikon buraya birebir aynı tanımlarla gömüldü (~10 KB) ve
|
||||||
|
* kabuk artık fa chunk'ına hiç dokunmuyor.
|
||||||
|
*
|
||||||
|
* Render çıktısı react-icons ile aynıdır: aynı `GenIcon` fabrikası, aynı
|
||||||
|
* viewBox/path verisi, aynı prop davranışı (size, color, title, IconContext).
|
||||||
|
*
|
||||||
|
* Yeni bir ikon gerekirse: node_modules/react-icons/fa/index.mjs içindeki
|
||||||
|
* tanımı buraya aynı biçimde kopyalayın.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const FaCaretDown: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCaretLeft: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 192 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M192 127.338v257.324c0 17.818-21.543 26.741-34.142 14.142L29.196 270.142c-7.81-7.81-7.81-20.474 0-28.284l128.662-128.662c12.599-12.6 34.142-3.676 34.142 14.142z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCaretRight: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 192 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCaretUp: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M288.662 352H31.338c-17.818 0-26.741-21.543-14.142-34.142l128.662-128.662c7.81-7.81 20.474-7.81 28.284 0l128.662 128.662c12.6 12.599 3.676 34.142-14.142 34.142z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCalendarAlt: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 448 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M0 464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V192H0v272zm320-196c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM192 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM64 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zM400 64h-48V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H160V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H48C21.5 64 0 85.5 0 112v48h448v-48c0-26.5-21.5-48-48-48z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCheck: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaCheckCircle: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronCircleLeft: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zM142.1 273l135.5 135.5c9.4 9.4 24.6 9.4 33.9 0l17-17c9.4-9.4 9.4-24.6 0-33.9L226.9 256l101.6-101.6c9.4-9.4 9.4-24.6 0-33.9l-17-17c-9.4-9.4-24.6-9.4-33.9 0L142.1 239c-9.4 9.4-9.4 24.6 0 34z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronCircleRight: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M256 8c137 0 248 111 248 248S393 504 256 504 8 393 8 256 119 8 256 8zm113.9 231L234.4 103.5c-9.4-9.4-24.6-9.4-33.9 0l-17 17c-9.4 9.4-9.4 24.6 0 33.9L285.1 256 183.5 357.6c-9.4 9.4-9.4 24.6 0 33.9l17 17c9.4 9.4 24.6 9.4 33.9 0L369.9 273c9.4-9.4 9.4-24.6 0-34z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronDown: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 448 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronLeft: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronRight: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaChevronUp: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 448 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaClock: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaEllipsisH: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M328 256c0 39.8-32.2 72-72 72s-72-32.2-72-72 32.2-72 72-72 72 32.2 72 72zm104-72c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72zm-352 0c-39.8 0-72 32.2-72 72s32.2 72 72 72 72-32.2 72-72-32.2-72-72-72z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaExclamation: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 192 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaFile: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 384 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm160-14.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaFileArchive: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 384 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M377 105L279.1 7c-4.5-4.5-10.6-7-17-7H256v128h128v-6.1c0-6.3-2.5-12.4-7-16.9zM128.4 336c-17.9 0-32.4 12.1-32.4 27 0 15 14.6 27 32.5 27s32.4-12.1 32.4-27-14.6-27-32.5-27zM224 136V0h-63.6v32h-32V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zM95.9 32h32v32h-32zm32.3 384c-33.2 0-58-30.4-51.4-62.9L96.4 256v-32h32v-32h-32v-32h32v-32h-32V96h32V64h32v32h-32v32h32v32h-32v32h32v32h-32v32h22.1c5.7 0 10.7 4.1 11.8 9.7l17.3 87.7c6.4 32.4-18.4 62.6-51.4 62.6z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaFilePdf: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 384 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M181.9 256.1c-5-16-4.9-46.9-2-46.9 8.4 0 7.6 36.9 2 46.9zm-1.7 47.2c-7.7 20.2-17.3 43.3-28.4 62.7 18.3-7 39-17.2 62.9-21.9-12.7-9.6-24.9-23.4-34.5-40.8zM86.1 428.1c0 .8 13.2-5.4 34.9-40.2-6.7 6.3-29.1 24.5-34.9 40.2zM248 160h136v328c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24V24C0 10.7 10.7 0 24 0h200v136c0 13.2 10.8 24 24 24zm-8 171.8c-20-12.2-33.3-29-42.7-53.8 4.5-18.5 11.6-46.6 6.2-64.2-4.7-29.4-42.4-26.5-47.8-6.8-5 18.3-.4 44.1 8.1 77-11.6 27.6-28.7 64.6-40.8 85.8-.1 0-.1.1-.2.1-27.1 13.9-73.6 44.5-54.5 68 5.6 6.9 16 10 21.5 10 17.9 0 35.7-18 61.1-61.8 25.8-8.5 54.1-19.1 79-23.2 21.7 11.8 47.1 19.5 64 19.5 29.2 0 31.2-32 19.7-43.4-13.9-13.6-54.3-9.7-73.6-7.2zM377 105L279 7c-4.5-4.5-10.6-7-17-7h-6v128h128v-6.1c0-6.3-2.5-12.4-7-16.9zm-74.1 255.3c4.1-2.7-2.5-11.9-42.8-9 37.1 15.8 42.8 9 42.8 9z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaInfoCircle: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaSort: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41zm255-105L177 64c-9.4-9.4-24.6-9.4-33.9 0L24 183c-15.1 15.1-4.4 41 17 41h238c21.4 0 32.1-25.9 17-41z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaSortDown: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaSortUp: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 320 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M279 224H41c-21.4 0-32.1-25.9-17-41L143 64c9.4-9.4 24.6-9.4 33.9 0l119 119c15.2 15.1 4.5 41-16.9 41z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaSpinner: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M304 48c0 26.51-21.49 48-48 48s-48-21.49-48-48 21.49-48 48-48 48 21.49 48 48zm-48 368c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zm208-208c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM96 256c0-26.51-21.49-48-48-48S0 229.49 0 256s21.49 48 48 48 48-21.49 48-48zm12.922 99.078c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.491-48-48-48zm294.156 0c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48c0-26.509-21.49-48-48-48zM108.922 60.922c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.491-48-48-48z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaTimes: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 352 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaTimesCircle: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaWindowMaximize: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M464 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-16 160H64v-84c0-6.6 5.4-12 12-12h360c6.6 0 12 5.4 12 12v84z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const FaWindowRestore: IconType = GenIcon({
|
||||||
|
tag: 'svg',
|
||||||
|
attr: { viewBox: '0 0 512 512' },
|
||||||
|
child: [
|
||||||
|
{
|
||||||
|
tag: 'path',
|
||||||
|
attr: {
|
||||||
|
d: 'M512 48v288c0 26.5-21.5 48-48 48h-48V176c0-44.1-35.9-80-80-80H128V48c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zM384 176v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V176c0-26.5 21.5-48 48-48h288c26.5 0 48 21.5 48 48zm-68 28c0-6.6-5.4-12-12-12H76c-6.6 0-12 5.4-12 12v52h252v-52z',
|
||||||
|
},
|
||||||
|
child: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
@ -151,30 +151,47 @@ div.dialog-after-open > div.dialog-content.maximized {
|
||||||
/* React Modal prevent bg scroll */
|
/* React Modal prevent bg scroll */
|
||||||
|
|
||||||
/* Window Controls Styles */
|
/* Window Controls Styles */
|
||||||
.window-controls {
|
.window-controls .window-controls-btn {
|
||||||
top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-controls .close-btn {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: all 0.2s ease;
|
transition:
|
||||||
|
background-color 0.2s ease,
|
||||||
|
color 0.2s ease;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.window-controls .close-btn:hover {
|
.window-controls .window-controls-btn:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.window-controls .close-btn:last-child:hover {
|
.window-controls .window-controls-btn:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-controls .window-controls-close:hover {
|
||||||
background-color: #e74c3c;
|
background-color: #e74c3c;
|
||||||
color: white;
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .window-controls .window-controls-btn {
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .window-controls .window-controls-btn:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.12);
|
||||||
|
color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .window-controls .window-controls-close:hover {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maximized dialog styles */
|
/* Maximized dialog styles */
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,81 @@
|
||||||
import * as fc from 'react-icons/fc'
|
import { useSyncExternalStore } from 'react'
|
||||||
import * as fa from 'react-icons/fa'
|
|
||||||
import type { ComponentType } from 'react'
|
import type { ComponentType } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menü ikonları veritabanında ada göre tutulduğu için (`FaUser`, `FcFolder` …)
|
||||||
|
* tüm react-icons setinin ad→bileşen sözlüğü olarak hazır olması gerekiyor.
|
||||||
|
*
|
||||||
|
* Bu setler `import * as` ile STATİK alındığında rollup tree-shaking'i tamamen
|
||||||
|
* kapanıyordu: modül tek parça olarak korunduğu için, uygulamada tek bir `FaCog`
|
||||||
|
* kullanan dosya bile ~1.3 MB'lık fa chunk'ını ilk açılış grafiğine sokuyordu
|
||||||
|
* (ölçüm: eager JS 1756 KB'ın 1281 KB'ı sadece bu).
|
||||||
|
*
|
||||||
|
* Çözüm: setler DİNAMİK import ile yükleniyor.
|
||||||
|
* - Named import kullanan (~190) dosya artık normal şekilde tree-shake ediliyor,
|
||||||
|
* yalnızca kullandıkları ikonlar bundle'a giriyor.
|
||||||
|
* - Tam sözlük ilk boyamayı bloklamayan ayrı bir chunk'a düşüyor; ilk abone
|
||||||
|
* olan bileşen (kenar çubuğu) yüklemeyi tetikler, hazır olunca `useSyncExternalStore`
|
||||||
|
* üzerinden yeniden render edilir. Menü verisi zaten API'den asenkron geldiği
|
||||||
|
* için pratikte ikonlar menüyle birlikte görünür.
|
||||||
|
*/
|
||||||
|
|
||||||
export type NavigationIcons = Record<string, ComponentType<any>>
|
export type NavigationIcons = Record<string, ComponentType<any>>
|
||||||
|
|
||||||
const navigationIcon: NavigationIcons = {}
|
/**
|
||||||
|
* Snapshot referansı yükleme bitince DEĞİŞİR; böylece `useSyncExternalStore`
|
||||||
|
* yeniden render tetikler ve tüketicilerdeki `useMemo(..., [navigationIcon])`
|
||||||
|
* bağımlılıkları da doğru şekilde tazelenir.
|
||||||
|
*/
|
||||||
|
let snapshot: NavigationIcons = {}
|
||||||
|
|
||||||
function registerIcons(iconModule: Record<string, any>) {
|
const listeners = new Set<() => void>()
|
||||||
for (const [key, Icon] of Object.entries(iconModule)) {
|
let loadPromise: Promise<void> | null = null
|
||||||
if (
|
|
||||||
Icon &&
|
function collectIcons(iconModules: Record<string, unknown>[]): NavigationIcons {
|
||||||
(typeof Icon === 'function' ||
|
const icons: NavigationIcons = {}
|
||||||
(typeof Icon === 'object' && 'render' in Icon))
|
for (const iconModule of iconModules) {
|
||||||
) {
|
for (const [key, Icon] of Object.entries(iconModule)) {
|
||||||
navigationIcon[key] = Icon as ComponentType<any>
|
if (Icon && (typeof Icon === 'function' || (typeof Icon === 'object' && 'render' in Icon))) {
|
||||||
|
icons[key] = Icon as ComponentType<any>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return icons
|
||||||
|
}
|
||||||
|
|
||||||
|
/** İkon sözlüğünü (bir kez) yükler. Birden çok çağrı aynı promise'i paylaşır. */
|
||||||
|
export function loadNavigationIcons(): Promise<void> {
|
||||||
|
if (!loadPromise) {
|
||||||
|
loadPromise = Promise.all([import('react-icons/fc'), import('react-icons/fa')])
|
||||||
|
.then((modules) => {
|
||||||
|
snapshot = collectIcons(modules)
|
||||||
|
listeners.forEach((listener) => listener())
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// Yükleme başarısızsa yeniden denenebilsin; ikonsuz menü çalışmaya devam eder.
|
||||||
|
loadPromise = null
|
||||||
|
console.error('Navigation icons could not be loaded', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribe(listener: () => void) {
|
||||||
|
listeners.add(listener)
|
||||||
|
void loadNavigationIcons()
|
||||||
|
return () => {
|
||||||
|
listeners.delete(listener)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerIcons(fc)
|
const getSnapshot = () => snapshot
|
||||||
registerIcons(fa)
|
|
||||||
|
|
||||||
export default navigationIcon
|
/**
|
||||||
|
* Ad→ikon sözlüğünü döner ve sözlük yüklendiğinde bileşeni yeniden render eder.
|
||||||
|
* Yükleme tamamlanana kadar boş sözlük döner; çağıran taraflar zaten
|
||||||
|
* "ikon bulunamadı" durumunu (fallback ikon / null) ele alıyor.
|
||||||
|
*/
|
||||||
|
export function useNavigationIcons(): NavigationIcons {
|
||||||
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { store } from '../store'
|
import { store } from '../store'
|
||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from '@/utils/jwt'
|
||||||
import appConfig from '../proxy/configs/app.config'
|
import appConfig from '../proxy/configs/app.config'
|
||||||
import { refreshToken, signOut as revokeTokens } from './auth.service'
|
import { refreshToken, signOut as revokeTokens } from './auth.service'
|
||||||
import { isLoginSuccess } from '../proxy/account/models'
|
import { isLoginSuccess } from '../proxy/account/models'
|
||||||
|
|
|
||||||
148
ui/src/utils/collection.ts
Normal file
148
ui/src/utils/collection.ts
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
* lodash yerine kullanılan minimal yardımcılar.
|
||||||
|
*
|
||||||
|
* Uygulamada lodash'ten yalnızca aşağıdaki dokuz fonksiyon kullanılıyordu;
|
||||||
|
* bunlar için ~25 KB'lık bir bağımlılık taşımak yerine davranışları birebir
|
||||||
|
* karşılayan yerel sürümler tutuluyor. Davranış farkı olmaması önemli:
|
||||||
|
* çağrı yerleri lodash semantiğine göre yazılmıştı.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** lodash/isNil */
|
||||||
|
export const isNil = (value: unknown): value is null | undefined => value == null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lodash/isEmpty — nesne, dizi, string, Map/Set ve primitive'ler için.
|
||||||
|
* Primitive (number/boolean) daima "boş" sayılır; lodash de böyle davranır.
|
||||||
|
*/
|
||||||
|
export const isEmpty = (value: unknown): boolean => {
|
||||||
|
if (value == null) return true
|
||||||
|
if (Array.isArray(value) || typeof value === 'string') return value.length === 0
|
||||||
|
if (value instanceof Map || value instanceof Set) return value.size === 0
|
||||||
|
if (typeof value === 'object') return Object.keys(value as object).length === 0
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** lodash/isPlainObject — düz nesne mi (dizi, Date, class örneği değil). */
|
||||||
|
export const isPlainObject = (value: unknown): boolean => {
|
||||||
|
if (typeof value !== 'object' || value === null) return false
|
||||||
|
const proto = Object.getPrototypeOf(value)
|
||||||
|
return proto === null || proto === Object.prototype
|
||||||
|
}
|
||||||
|
|
||||||
|
/** lodash/keys */
|
||||||
|
export const keys = (value: object | null | undefined): string[] =>
|
||||||
|
value == null ? [] : Object.keys(value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lodash/cloneDeep — düz veri (nesne/dizi/Date/Map/Set) için derin kopya.
|
||||||
|
* structuredClone fonksiyon ve React elemanı içeren yapılarda patlar; bu
|
||||||
|
* yüzden elle dolaşılıyor, klonlanamayan değerler referansla taşınıyor.
|
||||||
|
*/
|
||||||
|
export const cloneDeep = <T>(value: T, seen = new WeakMap<object, unknown>()): T => {
|
||||||
|
if (value === null || typeof value !== 'object') return value
|
||||||
|
|
||||||
|
const objectValue = value as unknown as object
|
||||||
|
const cached = seen.get(objectValue)
|
||||||
|
if (cached !== undefined) return cached as T
|
||||||
|
|
||||||
|
if (value instanceof Date) return new Date(value.getTime()) as unknown as T
|
||||||
|
if (value instanceof RegExp) return new RegExp(value.source, value.flags) as unknown as T
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
const copy: unknown[] = []
|
||||||
|
seen.set(objectValue, copy)
|
||||||
|
value.forEach((item, index) => {
|
||||||
|
copy[index] = cloneDeep(item, seen)
|
||||||
|
})
|
||||||
|
return copy as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Map) {
|
||||||
|
const copy = new Map()
|
||||||
|
seen.set(objectValue, copy)
|
||||||
|
value.forEach((item, key) => copy.set(key, cloneDeep(item, seen)))
|
||||||
|
return copy as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Set) {
|
||||||
|
const copy = new Set()
|
||||||
|
seen.set(objectValue, copy)
|
||||||
|
value.forEach((item) => copy.add(cloneDeep(item, seen)))
|
||||||
|
return copy as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
// File/Blob gibi klonlanamayan host nesneleri referansla taşınır.
|
||||||
|
if (!isPlainObject(value)) return value
|
||||||
|
|
||||||
|
const copy: Record<string, unknown> = {}
|
||||||
|
seen.set(objectValue, copy)
|
||||||
|
Object.entries(value as Record<string, unknown>).forEach(([key, item]) => {
|
||||||
|
copy[key] = cloneDeep(item, seen)
|
||||||
|
})
|
||||||
|
return copy as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lodash/get — 'a.b[0].c' biçimli yol desteğiyle güvenli okuma.
|
||||||
|
* Formik'in iç içe alan adları (`address.city`, `rows[0].name`) bu formatta.
|
||||||
|
*/
|
||||||
|
export const get = <T = unknown>(object: unknown, path: string, defaultValue?: T): T => {
|
||||||
|
if (object == null) return defaultValue as T
|
||||||
|
|
||||||
|
const segments = path
|
||||||
|
.replace(/\[(\d+)\]/g, '.$1')
|
||||||
|
.split('.')
|
||||||
|
.filter((segment) => segment.length > 0)
|
||||||
|
|
||||||
|
let current: unknown = object
|
||||||
|
for (const segment of segments) {
|
||||||
|
if (current == null) return defaultValue as T
|
||||||
|
current = (current as Record<string, unknown>)[segment]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (current === undefined ? defaultValue : current) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/** lodash/groupBy — anahtar üreticisi alan/fonksiyon olabilir. */
|
||||||
|
export const groupBy = <T>(
|
||||||
|
collection: readonly T[] | null | undefined,
|
||||||
|
iteratee: ((item: T) => string | number) | keyof T,
|
||||||
|
): Record<string, T[]> => {
|
||||||
|
const result: Record<string, T[]> = {}
|
||||||
|
if (!collection) return result
|
||||||
|
|
||||||
|
const resolveKey =
|
||||||
|
typeof iteratee === 'function'
|
||||||
|
? (item: T) => (iteratee as (item: T) => string | number)(item)
|
||||||
|
: (item: T) => (item as Record<string, unknown>)[iteratee as string] as string | number
|
||||||
|
|
||||||
|
for (const item of collection) {
|
||||||
|
const key = String(resolveKey(item))
|
||||||
|
;(result[key] ??= []).push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lodash/remove — diziyi YERİNDE değiştirir ve çıkarılan elemanları döner.
|
||||||
|
* Çağrı yerleri bu mutasyona güveniyor.
|
||||||
|
*/
|
||||||
|
export const remove = <T>(array: T[], predicate: (item: T) => boolean): T[] => {
|
||||||
|
const removed: T[] = []
|
||||||
|
for (let index = array.length - 1; index >= 0; index -= 1) {
|
||||||
|
if (predicate(array[index])) {
|
||||||
|
removed.unshift(array[index])
|
||||||
|
array.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
let uniqueIdCounter = 0
|
||||||
|
|
||||||
|
/** lodash/uniqueId */
|
||||||
|
export const uniqueId = (prefix = ''): string => {
|
||||||
|
uniqueIdCounter += 1
|
||||||
|
return `${prefix}${uniqueIdCounter}`
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { jwtDecode } from 'jwt-decode'
|
import { jwtDecode } from '@/utils/jwt'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import useQuery from './useQuery'
|
import useQuery from './useQuery'
|
||||||
import { store, useStoreActions, useStoreState } from '../../store/store'
|
import { store, useStoreActions, useStoreState } from '../../store/store'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
|
|
||||||
function useAuthority(
|
function useAuthority(
|
||||||
userAuthority: string[] = [],
|
userAuthority: string[] = [],
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { FaUser } from 'react-icons/fa'
|
import { FaUser } from 'react-icons/fa'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { navigationTreeToFlat } from '@/utils/navigation'
|
import { navigationTreeToFlat } from '@/utils/navigation'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ const extractListFormCode = (path: string): string | null => {
|
||||||
export function useCurrentMenuIcon(className = 'w-6 h-6'): ReactElement {
|
export function useCurrentMenuIcon(className = 'w-6 h-6'): ReactElement {
|
||||||
const mainMenu = useStoreState((state) => state.abpConfig.menu.mainMenu)
|
const mainMenu = useStoreState((state) => state.abpConfig.menu.mainMenu)
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
const menus = navigationTreeToFlat(mainMenu)
|
const menus = navigationTreeToFlat(mainMenu)
|
||||||
|
|
||||||
const currentMenu = menus.find((menu) => {
|
const currentMenu = menus.find((menu) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
// eslint-disable @typescript-eslint/no-explicit-any
|
// eslint-disable @typescript-eslint/no-explicit-any
|
||||||
import type { NavigationTree } from '@/proxy/menus/navigation'
|
import type { NavigationTree } from '@/proxy/menus/navigation'
|
||||||
import isPlainObject from 'lodash/isPlainObject'
|
import { isPlainObject } from '@/utils/collection'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
interface NavInfo extends NavigationTree {
|
interface NavInfo extends NavigationTree {
|
||||||
|
|
|
||||||
27
ui/src/utils/jwt.ts
Normal file
27
ui/src/utils/jwt.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* jwt-decode yerine kullanılan minimal çözücü.
|
||||||
|
*
|
||||||
|
* Sadece payload okunuyor; imza doğrulaması zaten sunucunun işi. Token
|
||||||
|
* base64url kodlu olduğu için `-`/`_` çevrilir ve padding tamamlanır;
|
||||||
|
* ayrıca Türkçe karakter içeren claim'lerin bozulmaması için atob çıktısı
|
||||||
|
* UTF-8 olarak yeniden çözülür.
|
||||||
|
*/
|
||||||
|
export const jwtDecode = <T = Record<string, unknown>>(token: string): T => {
|
||||||
|
const payload = token?.split('.')[1]
|
||||||
|
if (!payload) {
|
||||||
|
throw new Error('Invalid token specified: missing payload')
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')
|
||||||
|
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const binary = atob(padded)
|
||||||
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))
|
||||||
|
return JSON.parse(new TextDecoder().decode(bytes)) as T
|
||||||
|
} catch {
|
||||||
|
throw new Error('Invalid token specified: payload could not be decoded')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default jwtDecode
|
||||||
|
|
@ -3,8 +3,8 @@ import Loading from '@/components/shared/Loading'
|
||||||
import { Alert, Button } from '@/components/ui'
|
import { Alert, Button } from '@/components/ui'
|
||||||
import { store, useStoreState } from '@/store'
|
import { store, useStoreState } from '@/store'
|
||||||
import { Suspense, useEffect } from 'react'
|
import { Suspense, useEffect } from 'react'
|
||||||
import { ErrorBoundary } from 'react-error-boundary'
|
import ErrorBoundary from '@/components/shared/ErrorBoundary'
|
||||||
import type { FallbackProps } from 'react-error-boundary'
|
import type { FallbackProps } from '@/components/shared/ErrorBoundary'
|
||||||
import { FaArrowLeft } from 'react-icons/fa'
|
import { FaArrowLeft } from 'react-icons/fa'
|
||||||
import { Navigate, useLocation } from 'react-router-dom'
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -19,8 +19,8 @@ interface ViewsProps {
|
||||||
layout?: LayoutType
|
layout?: LayoutType
|
||||||
}
|
}
|
||||||
|
|
||||||
// react-error-boundary v6'dan itibaren `error` `unknown` tipinde geliyor;
|
// `error` `unknown` tipinde geliyor; throw edilen her şey Error olmayabilir,
|
||||||
// throw edilen her şey Error olmayabilir, o yüzden daraltıyoruz.
|
// o yüzden daraltıyoruz.
|
||||||
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const { name, message } =
|
const { name, message } =
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { NotificationDto } from '@/proxy/notification/models'
|
||||||
import { getList } from '@/services/notification.service'
|
import { getList } from '@/services/notification.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import Log from './components/Log'
|
import Log from './components/Log'
|
||||||
import LogFilter from './components/LogFilter'
|
import LogFilter from './components/LogFilter'
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ import { AVATAR_URL } from '@/constants/app.constant'
|
||||||
import { NotificationDto } from '@/proxy/notification/models'
|
import { NotificationDto } from '@/proxy/notification/models'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty, keys } from '@/utils/collection'
|
||||||
import keys from 'lodash/keys'
|
|
||||||
import Event from './Event'
|
import Event from './Event'
|
||||||
import TimeLineAvatar from './TimeLineAvatar'
|
import TimeLineAvatar from './TimeLineAvatar'
|
||||||
import useLocale from '@/utils/hooks/useLocale'
|
import useLocale from '@/utils/hooks/useLocale'
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import { JsonRowDialogData } from './json-row-operations/types'
|
||||||
import JsonRowOpDialogSeries from './json-row-operations/JsonRowOpDialogSeries'
|
import JsonRowOpDialogSeries from './json-row-operations/JsonRowOpDialogSeries'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
|
|
||||||
// Bu sekme ListForm nesnesinin tamamını düzenler; `name` / `title` / `height`
|
// Bu sekme ListForm nesnesinin tamamını düzenler; `name` / `title` / `height`
|
||||||
// gibi alanların girdisi burada yok. Şema bu alanları zorunlu tutuyordu ve
|
// gibi alanların girdisi burada yok. Şema bu alanları zorunlu tutuyordu ve
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { FormEditProps } from './FormEdit'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import type { GanttScaleType } from 'devextreme/ui/gantt'
|
import type { GanttScaleType } from 'devextreme/ui/gantt'
|
||||||
import { gantScaleOptions } from './options'
|
import { gantScaleOptions } from './options'
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { FormEditProps } from './FormEdit'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { firstDayOfWeekOptions, schedulerViewOptions } from './options'
|
import { firstDayOfWeekOptions, schedulerViewOptions } from './options'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import type { SelectBoxOption } from '@/types/shared'
|
import type { SelectBoxOption } from '@/types/shared'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import type { FormEditProps } from './FormEdit'
|
import type { FormEditProps } from './FormEdit'
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { FormEditProps } from './FormEdit'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({})
|
const validationSchema = Yup.object().shape({})
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
import { cascadeFilterOperator } from '../options'
|
import { cascadeFilterOperator } from '../options'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
import { getListFormFields } from '@/services/admin/list-form-field.service'
|
||||||
import groupBy from 'lodash/groupBy'
|
import { groupBy } from '@/utils/collection'
|
||||||
import { extraFilterControlTypeOptions } from '@/proxy/admin/list-form/options'
|
import { extraFilterControlTypeOptions } from '@/proxy/admin/list-form/options'
|
||||||
|
|
||||||
const schema = object().shape({
|
const schema = object().shape({
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { WizardFileInfoDto } from '@/proxy/admin/wizard/models'
|
import { WizardFileInfoDto } from '@/proxy/admin/wizard/models'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
|
|
||||||
interface ConfirmState {
|
interface ConfirmState {
|
||||||
fileName: string
|
fileName: string
|
||||||
|
|
@ -32,6 +32,7 @@ const WizardFileManager = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const mode = useStoreState((state) => state.theme.mode)
|
const mode = useStoreState((state) => state.theme.mode)
|
||||||
const MenuIcon = useCurrentMenuIcon('w-5 h-5')
|
const MenuIcon = useCurrentMenuIcon('w-5 h-5')
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
const [files, setFiles] = useState<WizardFileInfoDto[]>([])
|
const [files, setFiles] = useState<WizardFileInfoDto[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button, FormItem, Input, Notification, Select, toast } from '@/components/ui'
|
import { Button, FormItem, Input, Notification, Select, toast } from '@/components/ui'
|
||||||
import { MenuDto } from '@/proxy/menus/models'
|
import { MenuDto } from '@/proxy/menus/models'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
import { MenuService } from '@/services/menu.service'
|
import { MenuService } from '@/services/menu.service'
|
||||||
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
import { Field, FieldProps, FormikErrors, FormikTouched } from 'formik'
|
||||||
|
|
@ -132,6 +132,7 @@ function TreeNode({
|
||||||
const isExpanded = expanded.has(node.code)
|
const isExpanded = expanded.has(node.code)
|
||||||
const isSelected = node.code === selectedCode
|
const isSelected = node.code === selectedCode
|
||||||
const isEditing = editingCode === node.code
|
const isEditing = editingCode === node.code
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
const NodeIcon = node.icon ? navigationIcon[node.icon] : null
|
const NodeIcon = node.icon ? navigationIcon[node.icon] : null
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import TableNoRecords from '@/views/shared/TableNoRecords'
|
import TableNoRecords from '@/views/shared/TableNoRecords'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
import { Field, FieldProps, Form, Formik, FormikHelpers } from 'formik'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { memo, Suspense, useEffect, useMemo, useState } from 'react'
|
import { memo, Suspense, useEffect, useMemo, useState } from 'react'
|
||||||
import { NodeApi, NodeRendererProps } from 'react-arborist'
|
import { NodeApi, NodeRendererProps } from 'react-arborist'
|
||||||
import { Tree } from 'react-arborist/dist/module/components/tree'
|
import { Tree } from 'react-arborist/dist/module/components/tree'
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import Container from '@/components/shared/Container'
|
||||||
import { Button, Table, toast } from '@/components/ui'
|
import { Button, Table, toast } from '@/components/ui'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import PageTitle from '@/components/shared/PageTitle'
|
import PageTitle from '@/components/shared/PageTitle'
|
||||||
import RolesPermission from './RolesPermission'
|
import RolesPermission from './RolesPermission'
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { Table } from '@/components/ui'
|
||||||
import { TenantDto } from '@/proxy/config/models'
|
import { TenantDto } from '@/proxy/config/models'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import PageTitle from '@/components/shared/PageTitle'
|
import PageTitle from '@/components/shared/PageTitle'
|
||||||
import TenantsConnectionString from './TenantsConnectionString'
|
import TenantsConnectionString from './TenantsConnectionString'
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Table } from '@/components/ui'
|
||||||
import { getUsers } from '@/services/identity.service'
|
import { getUsers } from '@/services/identity.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import useThemeClass from '@/utils/hooks/useThemeClass'
|
import useThemeClass from '@/utils/hooks/useThemeClass'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import PageTitle from '@/components/shared/PageTitle'
|
import PageTitle from '@/components/shared/PageTitle'
|
||||||
import UsersPermission from './UsersPermission'
|
import UsersPermission from './UsersPermission'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useEffect, useRef } from 'react'
|
||||||
import { Alert, Button } from '@/components/ui'
|
import { Alert, Button } from '@/components/ui'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import useAccount from '@/utils/hooks/useAccount'
|
import useAccount from '@/utils/hooks/useAccount'
|
||||||
|
|
@ -12,12 +12,17 @@ const VerifyConfirmationCode = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
useQuery({
|
// Doğrulama tek seferlik bir yan etki; StrictMode'un çift render'ında
|
||||||
queryKey: ['verifyConfirmationCode', userId, token],
|
// isteğin iki kez gitmemesi için ref ile bir kereye kilitleniyor.
|
||||||
queryFn: () => verifyConfirmationCode(userId!, token!),
|
const verifiedKey = useRef<string | null>(null)
|
||||||
retry: false,
|
|
||||||
refetchOnWindowFocus: false,
|
useEffect(() => {
|
||||||
})
|
if (!userId || !token) return
|
||||||
|
const key = `${userId}|${token}`
|
||||||
|
if (verifiedKey.current === key) return
|
||||||
|
verifiedKey.current = key
|
||||||
|
verifyConfirmationCode(userId, token)
|
||||||
|
}, [userId, token, verifyConfirmationCode])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
|
||||||
import { getMenus } from '@/services/menu.service'
|
import { getMenus } from '@/services/menu.service'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
import { CascadeBehavior, SqlTableRelation, RelationshipType } from '@/proxy/developerKit/models'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
import {
|
import {
|
||||||
MenuTreeNode,
|
MenuTreeNode,
|
||||||
|
|
@ -1122,6 +1122,7 @@ function SimpleMenuTreeNode({
|
||||||
const hasChildren = node.children.length > 0
|
const hasChildren = node.children.length > 0
|
||||||
const isExpanded = expanded.has(node.code)
|
const isExpanded = expanded.has(node.code)
|
||||||
const isSelected = node.code === selectedCode
|
const isSelected = node.code === selectedCode
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
const NodeIcon = node.icon ? navigationIcon[node.icon] : null
|
const NodeIcon = node.icon ? navigationIcon[node.icon] : null
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Badge, Button, Dialog, Notification, toast } from '@/components/ui'
|
import { Badge, Button, Dialog, Notification, toast } from '@/components/ui'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import CustomStore from 'devextreme/data/custom_store'
|
import CustomStore from 'devextreme/data/custom_store'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
@ -162,6 +162,8 @@ const FormButtons = (props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
|
||||||
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
|
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
|
||||||
const hasVisibleToolbarButtons = toolbarData?.some(isToolbarButtonVisible)
|
const hasVisibleToolbarButtons = toolbarData?.some(isToolbarButtonVisible)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Lightbox from 'yet-another-react-lightbox'
|
import { createPortal } from 'react-dom'
|
||||||
import 'yet-another-react-lightbox/styles.css'
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import Video from 'yet-another-react-lightbox/plugins/video'
|
import { FaChevronLeft, FaChevronRight, FaTimes } from 'react-icons/fa'
|
||||||
import Zoom from 'yet-another-react-lightbox/plugins/zoom'
|
import { Button } from '@/components/ui'
|
||||||
import Counter from 'yet-another-react-lightbox/plugins/counter'
|
|
||||||
import 'yet-another-react-lightbox/plugins/counter.css'
|
|
||||||
import { SocialMediaDto } from '@/proxy/intranet/models'
|
import { SocialMediaDto } from '@/proxy/intranet/models'
|
||||||
|
|
||||||
interface MediaLightboxProps {
|
interface MediaLightboxProps {
|
||||||
|
|
@ -14,53 +12,153 @@ interface MediaLightboxProps {
|
||||||
startIndex?: number
|
startIndex?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MIN_ZOOM = 1
|
||||||
|
const MAX_ZOOM = 3
|
||||||
|
const ZOOM_STEP = 0.25
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sosyal duvar medya görüntüleyici.
|
||||||
|
*
|
||||||
|
* Daha önce `yet-another-react-lightbox` kullanıyordu; ihtiyaç duyulan yüzey
|
||||||
|
* (görsel gezinme + sayaç + tekerlek ile yakınlaştırma + video oynatma) burada
|
||||||
|
* zaten bağımlılık olan framer-motion ile karşılanıyor, böylece paket ve ona
|
||||||
|
* ait üç eklenti + CSS bundle'dan düşüyor.
|
||||||
|
*/
|
||||||
const MediaLightbox: React.FC<MediaLightboxProps> = ({
|
const MediaLightbox: React.FC<MediaLightboxProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
media,
|
media,
|
||||||
startIndex = 0
|
startIndex = 0,
|
||||||
}) => {
|
}) => {
|
||||||
const slides = React.useMemo(() => {
|
const isVideo = media.type === 'video'
|
||||||
if (media.type === 'video' && media.urls && media.urls.length > 0) {
|
const urls = React.useMemo(() => media.urls ?? [], [media])
|
||||||
return [
|
const [index, setIndex] = React.useState(startIndex)
|
||||||
{
|
const [zoom, setZoom] = React.useState(MIN_ZOOM)
|
||||||
type: 'video' as const,
|
|
||||||
sources: [
|
// Aynı bileşen farklı medya/indeks ile yeniden açılabiliyor.
|
||||||
{
|
React.useEffect(() => {
|
||||||
src: media.urls[0],
|
if (isOpen) {
|
||||||
type: 'video/mp4'
|
setIndex(startIndex)
|
||||||
}
|
setZoom(MIN_ZOOM)
|
||||||
]
|
}
|
||||||
}
|
}, [isOpen, startIndex, media])
|
||||||
]
|
|
||||||
|
const step = React.useCallback(
|
||||||
|
(delta: number) => {
|
||||||
|
if (urls.length < 2) return
|
||||||
|
setIndex((current) => (current + delta + urls.length) % urls.length)
|
||||||
|
setZoom(MIN_ZOOM)
|
||||||
|
},
|
||||||
|
[urls.length],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Klavye ile gezinme ve kapatma; lightbox açıkken sayfa kaydırması kilitlenir.
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isOpen) return
|
||||||
|
|
||||||
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') onClose()
|
||||||
|
if (event.key === 'ArrowLeft') step(-1)
|
||||||
|
if (event.key === 'ArrowRight') step(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const urls = media.urls || []
|
const previousOverflow = document.body.style.overflow
|
||||||
return urls.map((url) => ({
|
document.body.style.overflow = 'hidden'
|
||||||
src: url
|
window.addEventListener('keydown', onKeyDown)
|
||||||
}))
|
|
||||||
}, [media])
|
|
||||||
|
|
||||||
return (
|
return () => {
|
||||||
<Lightbox
|
document.body.style.overflow = previousOverflow
|
||||||
open={isOpen}
|
window.removeEventListener('keydown', onKeyDown)
|
||||||
close={onClose}
|
}
|
||||||
slides={slides}
|
}, [isOpen, onClose, step])
|
||||||
index={startIndex}
|
|
||||||
plugins={[Video, Zoom, Counter]}
|
const onWheel = (event: React.WheelEvent) => {
|
||||||
counter={{ container: { style: { top: 'unset', bottom: 0 } } }}
|
if (isVideo) return
|
||||||
zoom={{
|
setZoom((current) =>
|
||||||
maxZoomPixelRatio: 3,
|
Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, current - Math.sign(event.deltaY) * ZOOM_STEP)),
|
||||||
scrollToZoom: true
|
)
|
||||||
}}
|
}
|
||||||
video={{
|
|
||||||
controls: true,
|
const stop = (event: React.MouseEvent) => event.stopPropagation()
|
||||||
autoPlay: false
|
|
||||||
}}
|
return createPortal(
|
||||||
styles={{
|
<AnimatePresence>
|
||||||
container: { backgroundColor: 'rgba(0, 0, 0, 0.95)' }
|
{isOpen && (
|
||||||
}}
|
<motion.div
|
||||||
/>
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 z-[1100] flex items-center justify-center bg-black/95"
|
||||||
|
onClick={onClose}
|
||||||
|
onWheel={onWheel}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
shape="circle"
|
||||||
|
icon={<FaTimes className="h-7 w-7" />}
|
||||||
|
className="absolute top-4 right-4 z-10 !h-12 !w-12 !px-0 text-white hover:text-gray-300"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{!isVideo && urls.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
shape="circle"
|
||||||
|
icon={<FaChevronLeft className="h-6 w-6" />}
|
||||||
|
className="absolute left-4 top-1/2 z-10 !h-12 !w-12 -translate-y-1/2 !px-0 bg-black/50 text-white hover:!bg-black/70"
|
||||||
|
onClick={(event) => {
|
||||||
|
stop(event)
|
||||||
|
step(-1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="plain"
|
||||||
|
shape="circle"
|
||||||
|
icon={<FaChevronRight className="h-6 w-6" />}
|
||||||
|
className="absolute right-4 top-1/2 z-10 !h-12 !w-12 -translate-y-1/2 !px-0 bg-black/50 text-white hover:!bg-black/70"
|
||||||
|
onClick={(event) => {
|
||||||
|
stop(event)
|
||||||
|
step(1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-4 left-1/2 z-10 -translate-x-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white">
|
||||||
|
{index + 1} / {urls.length}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isVideo
|
||||||
|
? urls[0] && (
|
||||||
|
<video
|
||||||
|
controls
|
||||||
|
playsInline
|
||||||
|
src={urls[0]}
|
||||||
|
className="max-h-[90vh] max-w-[90vw] rounded-lg"
|
||||||
|
onClick={stop}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: urls[index] && (
|
||||||
|
<motion.img
|
||||||
|
key={index}
|
||||||
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
|
animate={{ opacity: 1, scale: zoom }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
src={urls[index]}
|
||||||
|
alt={`${index + 1}`}
|
||||||
|
draggable={false}
|
||||||
|
className="max-h-[90vh] max-w-[90vw] rounded-lg object-contain"
|
||||||
|
style={{ cursor: zoom > MIN_ZOOM ? 'zoom-out' : 'zoom-in' }}
|
||||||
|
onClick={(event) => {
|
||||||
|
stop(event)
|
||||||
|
setZoom((current) => (current > MIN_ZOOM ? MIN_ZOOM : 2))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>,
|
||||||
|
document.body,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import {
|
||||||
import { ConfirmDialog } from '@/components/shared'
|
import { ConfirmDialog } from '@/components/shared'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
import { MenuDto } from '@/proxy/menus/models'
|
import { MenuDto } from '@/proxy/menus/models'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { MenuService } from '@/services/menu.service'
|
import { MenuService } from '@/services/menu.service'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
|
@ -102,6 +102,7 @@ const MenuItemComponentBase: React.FC<MenuItemComponentProps> = ({
|
||||||
|
|
||||||
const childCount = item.children?.length ?? 0
|
const childCount = item.children?.length ?? 0
|
||||||
const hasChildren = childCount > 0
|
const hasChildren = childCount > 0
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
const IconComponent = (item.icon && navigationIcon[item.icon]) || FaQuestionCircle
|
const IconComponent = (item.icon && navigationIcon[item.icon]) || FaQuestionCircle
|
||||||
const displayLabel = item.displayName ? translate('::' + item.displayName) : (item.code ?? '')
|
const displayLabel = item.displayName ? translate('::' + item.displayName) : (item.code ?? '')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ const Blog: React.FC = () => {
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
placeholder={translateLabel(translate, 'Public.blog.searchPlaceholder')}
|
placeholder={translateLabel(translate, 'Public.blog.SearchPlaceholder')}
|
||||||
aria-label={translate('::App.Platform.Search3')}
|
aria-label={translate('::App.Platform.Search3')}
|
||||||
className="w-full rounded-lg border border-gray-300 bg-white py-2 pl-10 text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
className="w-full rounded-lg border border-gray-300 bg-white py-2 pl-10 text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-400"
|
||||||
onChange={(event) => setSearchQuery(event.target.value)}
|
onChange={(event) => setSearchQuery(event.target.value)}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Loading from '@/components/shared/Loading'
|
import Loading from '@/components/shared/Loading'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
|
|
||||||
interface PageIconProps {
|
interface PageIconProps {
|
||||||
name?: string
|
name?: string
|
||||||
|
|
@ -10,6 +10,7 @@ interface PageIconProps {
|
||||||
|
|
||||||
/** Ikon anahtari tanimli degilse hicbir sey render etmez. */
|
/** Ikon anahtari tanimli degilse hicbir sey render etmez. */
|
||||||
export const PageIcon: React.FC<PageIconProps> = ({ name, className, size }) => {
|
export const PageIcon: React.FC<PageIconProps> = ({ name, className, size }) => {
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
const IconComponent = name ? navigationIcon[name] : undefined
|
const IconComponent = name ? navigationIcon[name] : undefined
|
||||||
|
|
||||||
if (!IconComponent) {
|
if (!IconComponent) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { useStoreActions, useStoreState } from '@/store'
|
||||||
import { SelectBoxOption } from '@/types/shared'
|
import { SelectBoxOption } from '@/types/shared'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Field, FieldProps, Form, Formik } from 'formik'
|
import { Field, FieldProps, Form, Formik } from 'formik'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import { isEmpty } from '@/utils/collection'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import PageTitle from '@/components/shared/PageTitle'
|
import PageTitle from '@/components/shared/PageTitle'
|
||||||
import { FaQuestionCircle } from 'react-icons/fa'
|
import { FaQuestionCircle } from 'react-icons/fa'
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { Button, Dialog, Notification, toast } from '@/components/ui'
|
||||||
import { MenuDto } from '@/proxy/menus/models'
|
import { MenuDto } from '@/proxy/menus/models'
|
||||||
import { MenuItem } from '@/proxy/menus/menu'
|
import { MenuItem } from '@/proxy/menus/menu'
|
||||||
import { MenuService } from '@/services/menu.service'
|
import { MenuService } from '@/services/menu.service'
|
||||||
import navigationIcon from '@/proxy/menus/navigation-icon.config'
|
import { useNavigationIcons } from '@/proxy/menus/navigation-icon.config'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { FaChevronDown, FaPlus } from 'react-icons/fa'
|
import { FaChevronDown, FaPlus } from 'react-icons/fa'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { useConfig } from '@/components/ui/ConfigProvider'
|
import { useConfig } from '@/components/ui/ConfigProvider'
|
||||||
|
|
@ -19,10 +19,12 @@ interface IconPickerFieldProps {
|
||||||
invalid?: boolean
|
invalid?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALL_ICON_ENTRIES = Object.entries(navigationIcon)
|
|
||||||
const ICON_PAGE_SIZE = 100
|
const ICON_PAGE_SIZE = 100
|
||||||
|
|
||||||
export function IconPickerField({ value, onChange, invalid }: IconPickerFieldProps) {
|
export function IconPickerField({ value, onChange, invalid }: IconPickerFieldProps) {
|
||||||
|
const navigationIcon = useNavigationIcons()
|
||||||
|
// Sözlük asenkron dolduğu için liste yükleme bittiğinde yeniden türetilir.
|
||||||
|
const allIconEntries = useMemo(() => Object.entries(navigationIcon), [navigationIcon])
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const [limit, setLimit] = useState(ICON_PAGE_SIZE)
|
const [limit, setLimit] = useState(ICON_PAGE_SIZE)
|
||||||
|
|
@ -36,8 +38,8 @@ export function IconPickerField({ value, onChange, invalid }: IconPickerFieldPro
|
||||||
|
|
||||||
const SelectedIcon = value ? navigationIcon[value] : null
|
const SelectedIcon = value ? navigationIcon[value] : null
|
||||||
const filtered = search.trim()
|
const filtered = search.trim()
|
||||||
? ALL_ICON_ENTRIES.filter(([key]) => key.toLowerCase().includes(search.toLowerCase()))
|
? allIconEntries.filter(([key]) => key.toLowerCase().includes(search.toLowerCase()))
|
||||||
: ALL_ICON_ENTRIES
|
: allIconEntries
|
||||||
const displayed = filtered.slice(0, limit)
|
const displayed = filtered.slice(0, limit)
|
||||||
const hasMore = displayed.length < filtered.length
|
const hasMore = displayed.length < filtered.length
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
|
||||||
|
|
@ -187,14 +187,12 @@ export default defineConfig(({ mode }) => {
|
||||||
'react-dom',
|
'react-dom',
|
||||||
'react-dom/client',
|
'react-dom/client',
|
||||||
'react-router-dom',
|
'react-router-dom',
|
||||||
'@tanstack/react-query',
|
|
||||||
'@tanstack/react-table',
|
'@tanstack/react-table',
|
||||||
'axios',
|
'axios',
|
||||||
'dayjs',
|
'dayjs',
|
||||||
'formik',
|
'formik',
|
||||||
'yup',
|
'yup',
|
||||||
'classnames',
|
'classnames',
|
||||||
'lodash',
|
|
||||||
'easy-peasy',
|
'easy-peasy',
|
||||||
'framer-motion',
|
'framer-motion',
|
||||||
'react-icons/fa',
|
'react-icons/fa',
|
||||||
|
|
@ -246,7 +244,7 @@ export default defineConfig(({ mode }) => {
|
||||||
// ayrılır; bunlar sürümler arası değişmediği için tarayıcı cache'inde
|
// ayrılır; bunlar sürümler arası değişmediği için tarayıcı cache'inde
|
||||||
// kalır ve deploy sonrası yeniden indirilmez.
|
// kalır ve deploy sonrası yeniden indirilmez.
|
||||||
//
|
//
|
||||||
// Not: framer-motion / lodash / formik gibi paketleri de buraya eklemek
|
// Not: framer-motion / formik gibi paketleri de buraya eklemek
|
||||||
// ölçüldü ve zararlı çıktı — Rollup'ın otomatik chunk'lamasında bu
|
// ölçüldü ve zararlı çıktı — Rollup'ın otomatik chunk'lamasında bu
|
||||||
// paketlerin bir kısmı yalnızca lazy view'lara giriyor; tek chunk'a
|
// paketlerin bir kısmı yalnızca lazy view'lara giriyor; tek chunk'a
|
||||||
// toplandıklarında ilk yükleme ~650 KB büyüyor.
|
// toplandıklarında ilk yükleme ~650 KB büyüyor.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue