i18n ve Localization düzenlemesi

This commit is contained in:
Sedat ÖZTÜRK 2025-08-12 12:39:09 +03:00
parent 00eb20d55f
commit 012f507cd3
42 changed files with 1462 additions and 678 deletions

View file

@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.4akve1tmvk" "revision": "0.ettrttb2g3"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

1500
ui/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -44,7 +44,6 @@
"formik": "^2.4.6", "formik": "^2.4.6",
"framer-motion": "^11.15.0", "framer-motion": "^11.15.0",
"history": "^5.3.0", "history": "^5.3.0",
"i18next": "^23.7.11",
"jspdf": "^3.0.1", "jspdf": "^3.0.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -57,7 +56,6 @@
"react-error-boundary": "^5.0.0", "react-error-boundary": "^5.0.0",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-highlight-words": "^0.20.0", "react-highlight-words": "^0.20.0",
"react-i18next": "^15.2.0",
"react-icons": "^5.4.0", "react-icons": "^5.4.0",
"react-modal": "^3.16.3", "react-modal": "^3.16.3",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",

View file

@ -1,31 +1,31 @@
import { MenuDto } from "@/proxy/menus"; import { MenuDto } from '@/proxy/menus/models'
export interface MenuItem extends MenuDto { export interface MenuItem extends MenuDto {
children?: MenuItem[]; children?: MenuItem[]
} }
export interface MenuApiResponse { export interface MenuApiResponse {
totalCount: number; totalCount: number
items: MenuItem[]; items: MenuItem[]
} }
export interface DragEndEvent { export interface DragEndEvent {
active: { active: {
id: string; id: string
data: { data: {
current: { current: {
type: string; type: string
item: MenuItem; item: MenuItem
}; }
}; }
}; }
over: { over: {
id: string; id: string
data: { data: {
current: { current: {
type: string; type: string
item?: MenuItem; item?: MenuItem
}; }
}; }
} | null; } | null
} }

View file

@ -3,7 +3,6 @@ import Theme from '@/components/template/Theme'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' 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 './locales'
import { store } from './store' import { store } from './store'
import { DynamicRoutesProvider } from './routes/dynamicRoutesContext' import { DynamicRoutesProvider } from './routes/dynamicRoutesContext'
import { ComponentProvider } from './contexts/ComponentContext' import { ComponentProvider } from './contexts/ComponentContext'

View file

@ -14,9 +14,9 @@ import {
import { FileUploadArea } from './FileUploadArea' import { FileUploadArea } from './FileUploadArea'
import { ImportPreview } from './ImportPreview' import { ImportPreview } from './ImportPreview'
import { ImportProgress } from './ImportProgress' import { ImportProgress } from './ImportProgress'
import { GridDto } from '@/proxy/form'
import { ListFormImportDto, ListFormImportExecuteDto } from '@/proxy/imports/models' import { ListFormImportDto, ListFormImportExecuteDto } from '@/proxy/imports/models'
import { ImportService } from '@/services/import.service' import { ImportService } from '@/services/import.service'
import { GridDto } from '@/proxy/form/models'
interface ImportDashboardProps { interface ImportDashboardProps {
gridDto: GridDto gridDto: GridDto
@ -97,17 +97,26 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
if (session.status !== 'failed') { if (session.status !== 'failed') {
// Daha uzun süreli ve gerçekçi bir progression // Daha uzun süreli ve gerçekçi bir progression
setTimeout(async () => { setTimeout(async () => {
session = await importService.updateSession(session.id, { status: 'validating', listFormCode: gridDto.gridOptions.listFormCode || '' }) session = await importService.updateSession(session.id, {
status: 'validating',
listFormCode: gridDto.gridOptions.listFormCode || '',
})
setCurrentSession(session) setCurrentSession(session)
}, 1000) // 1 saniye sonra validating }, 1000) // 1 saniye sonra validating
setTimeout(async () => { setTimeout(async () => {
session = await importService.updateSession(session.id, { status: 'processing', listFormCode: gridDto.gridOptions.listFormCode || '' }) session = await importService.updateSession(session.id, {
status: 'processing',
listFormCode: gridDto.gridOptions.listFormCode || '',
})
setCurrentSession(session) setCurrentSession(session)
}, 2000) // 2 saniye sonra processing başlangıç }, 2000) // 2 saniye sonra processing başlangıç
setTimeout(async () => { setTimeout(async () => {
session = await importService.updateSession(session.id, { status: 'uploaded', listFormCode: gridDto.gridOptions.listFormCode || '' }) session = await importService.updateSession(session.id, {
status: 'uploaded',
listFormCode: gridDto.gridOptions.listFormCode || '',
})
setCurrentSession(session) setCurrentSession(session)
}, 3000) // 3 saniye sonra uploaded }, 3000) // 3 saniye sonra uploaded
@ -199,7 +208,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
const getEditableColumns = () => { const getEditableColumns = () => {
return gridDto.columnFormats.filter( return gridDto.columnFormats.filter(
(col) => (col: any) =>
col.visible && col.visible &&
col.columnEditingDto.allowEditing && col.columnEditingDto.allowEditing &&
col.canCreate && col.canCreate &&
@ -287,7 +296,7 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-slate-100"> <tbody className="divide-y divide-slate-100">
{editableColumns.map((column) => ( {editableColumns.map((column: any) => (
<tr key={column.fieldName} className="hover:bg-slate-50"> <tr key={column.fieldName} className="hover:bg-slate-50">
<td className="px-2 py-2 font-medium text-slate-800"> <td className="px-2 py-2 font-medium text-slate-800">
{column.captionName || column.fieldName} {column.captionName || column.fieldName}
@ -308,7 +317,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
</span> </span>
</td> </td>
<td className="px-4 py-2"> <td className="px-4 py-2">
{column.validationRuleDto.some((rule) => rule.type === 'required') ? ( {column.validationRuleDto.some(
(rule: any) => rule.type === 'required',
) ? (
<span className="text-red-500 font-medium">Yes</span> <span className="text-red-500 font-medium">Yes</span>
) : ( ) : (
<span className="text-slate-400">No</span> <span className="text-slate-400">No</span>
@ -455,7 +466,9 @@ export const ImportDashboard: React.FC<ImportDashboardProps> = ({ gridDto }) =>
if (sessionExecutes[session.id]) { if (sessionExecutes[session.id]) {
setLoadingExecutes((prev) => new Set([...prev, session.id])) setLoadingExecutes((prev) => new Set([...prev, session.id]))
try { try {
const executes = await importService.getListFormImportExecutes(session.id) const executes = await importService.getListFormImportExecutes(
session.id,
)
setSessionExecutes((prev) => ({ setSessionExecutes((prev) => ({
...prev, ...prev,
[session.id]: executes, [session.id]: executes,

View file

@ -1,13 +1,17 @@
import React, { useState, useEffect, useRef } from 'react' import React, { useState, useEffect, useRef } from 'react'
import { CheckCircle, AlertTriangle, Eye, Play, X } from 'lucide-react' import { CheckCircle, AlertTriangle, Eye, Play, X } from 'lucide-react'
import { ListFormImportDto } from '@/proxy/imports/models' import { ListFormImportDto } from '@/proxy/imports/models'
import { GridDto } from '@/proxy/form' import { GridDto } from '@/proxy/form/models'
import { ImportService } from '@/services/import.service' import { ImportService } from '@/services/import.service'
interface ImportPreviewProps { interface ImportPreviewProps {
session: ListFormImportDto session: ListFormImportDto
gridDto: GridDto gridDto: GridDto
onExecute: (sessionId: string, listFormCode: string, selectedRows?: number[]) => Promise<{ successCount?: number } | void> onExecute: (
sessionId: string,
listFormCode: string,
selectedRows?: number[],
) => Promise<{ successCount?: number } | void>
loading: boolean loading: boolean
importService: ImportService importService: ImportService
onPreviewLoaded?: () => void onPreviewLoaded?: () => void
@ -20,13 +24,15 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
loading, loading,
importService, importService,
onPreviewLoaded, onPreviewLoaded,
}) => { }) => {
const [previewData, setPreviewData] = useState<any>(null) const [previewData, setPreviewData] = useState<any>(null)
const [selectedRows, setSelectedRows] = useState<number[]>([]) const [selectedRows, setSelectedRows] = useState<number[]>([])
const [selectAll, setSelectAll] = useState(false) const [selectAll, setSelectAll] = useState(false)
const [showSuccessMessage, setShowSuccessMessage] = useState(false) const [showSuccessMessage, setShowSuccessMessage] = useState(false)
const [lastExecutionResult, setLastExecutionResult] = useState<{ selectedCount: number; successCount: number } | null>(null) const [lastExecutionResult, setLastExecutionResult] = useState<{
selectedCount: number
successCount: number
} | null>(null)
const hasCalledOnPreviewLoaded = useRef(false) const hasCalledOnPreviewLoaded = useRef(false)
// Selection handlers // Selection handlers
@ -173,9 +179,7 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
{/* Preview Data */} {/* Preview Data */}
{previewData && previewData.headers && previewData.headers.length > 0 ? ( {previewData && previewData.headers && previewData.headers.length > 0 ? (
<div className="p-3 border-b border-slate-200"> <div className="p-3 border-b border-slate-200">
<h4 className="font-semibold text-slate-800 mb-4"> <h4 className="font-semibold text-slate-800 mb-4">Data Preview</h4>
Data Preview
</h4>
<div className="overflow-auto border border-slate-200 rounded-lg max-h-90"> <div className="overflow-auto border border-slate-200 rounded-lg max-h-90">
<table className="w-full text-sm min-w-full"> <table className="w-full text-sm min-w-full">
@ -257,7 +261,8 @@ export const ImportPreview: React.FC<ImportPreviewProps> = ({
<div className="flex items-center space-x-2 text-green-700"> <div className="flex items-center space-x-2 text-green-700">
<CheckCircle className="w-5 h-5" /> <CheckCircle className="w-5 h-5" />
<span className="font-medium"> <span className="font-medium">
Import completed successfully! {lastExecutionResult.successCount} of {lastExecutionResult.selectedCount} rows were imported. Import completed successfully! {lastExecutionResult.successCount} of{' '}
{lastExecutionResult.selectedCount} rows were imported.
</span> </span>
</div> </div>
</div> </div>

View file

@ -6,13 +6,13 @@ import type { ReactNode, ReactElement } from 'react'
import type { CommonProps } from '@/@types/common' import type { CommonProps } from '@/@types/common'
import { HiArrowLeft, HiCheck } from 'react-icons/hi' import { HiArrowLeft, HiCheck } from 'react-icons/hi'
import { Avatar, Select } from '@/components/ui' import { Avatar, Select } from '@/components/ui'
import i18n, { dateLocales } from '@/locales'
import { useStoreActions, useStoreState } from '@/store' import { useStoreActions, useStoreState } from '@/store'
import appConfig from '@/configs/app.config' import appConfig from '@/configs/app.config'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { components } from 'react-select' import { components } from 'react-select'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { hasSubdomain } from '@/utils/subdomain' import { hasSubdomain } from '@/utils/subdomain'
import { dateLocales } from '@/constants/dateLocales.constant'
interface SimpleProps extends CommonProps { interface SimpleProps extends CommonProps {
content?: ReactNode content?: ReactNode
@ -38,7 +38,6 @@ const Simple = ({ children, content, ...rest }: SimpleProps) => {
const onLanguageSelect = (cultureName = appConfig.locale) => { const onLanguageSelect = (cultureName = appConfig.locale) => {
const dispatchLang = () => { const dispatchLang = () => {
i18n.changeLanguage(cultureName)
setLang(cultureName) setLang(cultureName)
} }

View file

@ -7,10 +7,10 @@ import {
NAV_ITEM_TYPE_TITLE, NAV_ITEM_TYPE_TITLE,
} from '@/constants/navigation.constant' } from '@/constants/navigation.constant'
import { useStoreState } from '@/store' import { useStoreState } from '@/store'
import { useTranslation } from 'react-i18next'
import HorizontalMenuDropdownItem from './HorizontalMenuDropdownItem' import HorizontalMenuDropdownItem from './HorizontalMenuDropdownItem'
import HorizontalMenuItem from './HorizontalMenuItem' import HorizontalMenuItem from './HorizontalMenuItem'
import HorizontalMenuIcon from './HorizontalMenuIcon' import HorizontalMenuIcon from './HorizontalMenuIcon'
import { useLocalization } from '@/utils/hooks/useLocalization'
type HorizontalMenuContentProps = { type HorizontalMenuContentProps = {
manuVariant: NavMode manuVariant: NavMode
@ -18,8 +18,6 @@ type HorizontalMenuContentProps = {
} }
const HorizontalMenuContent = ({ manuVariant }: HorizontalMenuContentProps) => { const HorizontalMenuContent = ({ manuVariant }: HorizontalMenuContentProps) => {
const { t } = useTranslation()
const { mainMenu: navigationConfig } = useStoreState((state) => state.abpConfig.menu) const { mainMenu: navigationConfig } = useStoreState((state) => state.abpConfig.menu)
return ( return (
@ -45,7 +43,7 @@ const HorizontalMenuContent = ({ manuVariant }: HorizontalMenuContentProps) => {
title={ title={
<div className="flex items-center"> <div className="flex items-center">
<HorizontalMenuIcon icon={secondarySubNav.icon} /> <HorizontalMenuIcon icon={secondarySubNav.icon} />
<span>{t(secondarySubNav.translateKey, secondarySubNav.title)}</span> <span>{secondarySubNav.title}</span>
</div> </div>
} }
> >

View file

@ -1,7 +1,6 @@
import Dropdown from '@/components/ui/Dropdown' import Dropdown from '@/components/ui/Dropdown'
import HorizontalMenuNavLink from './HorizontalMenuNavLink' import HorizontalMenuNavLink from './HorizontalMenuNavLink'
import classNames from 'classnames' import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
export type HorizontalMenuItemProps = { export type HorizontalMenuItemProps = {
nav: { nav: {
@ -15,36 +14,21 @@ export type HorizontalMenuItemProps = {
} }
const HorizontalMenuDropdownItem = ({ nav }: HorizontalMenuItemProps) => { const HorizontalMenuDropdownItem = ({ nav }: HorizontalMenuItemProps) => {
const { title, translateKey, path, key, isExternalLink, icon } = nav const { title, path, key, isExternalLink, icon } = nav
const { t } = useTranslation()
const itemTitle = t(translateKey, title)
return ( return (
<Dropdown.Item <Dropdown.Item eventKey={key} className={classNames(path)}>
eventKey={key}
className={
classNames(
path
)
}
>
{path ? ( {path ? (
<HorizontalMenuNavLink <HorizontalMenuNavLink
path={path} path={path}
className={ className={classNames(path)}
classNames(
path
)
}
isExternalLink={isExternalLink} isExternalLink={isExternalLink}
icon={icon} icon={icon}
> >
{itemTitle} {title}
</HorizontalMenuNavLink> </HorizontalMenuNavLink>
) : ( ) : (
<span>{itemTitle}</span> <span>{title}</span>
)} )}
</Dropdown.Item> </Dropdown.Item>
) )

View file

@ -1,7 +1,6 @@
import navigationIcon from '@/configs/navigation-icon.config' import navigationIcon from '@/configs/navigation-icon.config'
import MenuItem from '@/components/ui/MenuItem' import MenuItem from '@/components/ui/MenuItem'
import HorizontalMenuNavLink from './HorizontalMenuNavLink' import HorizontalMenuNavLink from './HorizontalMenuNavLink'
import { useTranslation } from 'react-i18next'
import type { NavMode } from '@/@types/theme' import type { NavMode } from '@/@types/theme'
export type HorizontalMenuItemProps = { export type HorizontalMenuItemProps = {
@ -17,16 +16,8 @@ export type HorizontalMenuItemProps = {
manuVariant: NavMode manuVariant: NavMode
} }
const HorizontalMenuItem = ({ const HorizontalMenuItem = ({ nav, isLink, manuVariant }: HorizontalMenuItemProps) => {
nav, const { title, icon, path, isExternalLink } = nav
isLink,
manuVariant,
}: HorizontalMenuItemProps) => {
const { title, translateKey, icon, path, isExternalLink } = nav
const { t } = useTranslation()
const itemTitle = t(translateKey, title)
const renderIcon = icon && <span className="text-2xl">{navigationIcon[icon]}</span> const renderIcon = icon && <span className="text-2xl">{navigationIcon[icon]}</span>
@ -37,14 +28,14 @@ const HorizontalMenuItem = ({
<MenuItem variant={manuVariant}> <MenuItem variant={manuVariant}>
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
{renderIcon} {renderIcon}
{itemTitle} {title}
</span> </span>
</MenuItem> </MenuItem>
</HorizontalMenuNavLink> </HorizontalMenuNavLink>
) : ( ) : (
<MenuItem variant={manuVariant}> <MenuItem variant={manuVariant}>
{renderIcon} {renderIcon}
<span>{itemTitle}</span> <span>{title}</span>
</MenuItem> </MenuItem>
)} )}
</> </>

View file

@ -5,13 +5,11 @@ import Spinner from '@/components/ui/Spinner'
import classNames from 'classnames' import classNames from 'classnames'
import withHeaderItem from '@/utils/hoc/withHeaderItem' import withHeaderItem from '@/utils/hoc/withHeaderItem'
import { useStoreState, useStoreActions } from '@/store' import { useStoreState, useStoreActions } from '@/store'
import { dateLocales } from '@/locales'
import dayjs from 'dayjs' import dayjs from 'dayjs'
// eslint-disable-next-line import/no-named-as-default
import i18n from 'i18next'
import { HiCheck } from 'react-icons/hi' import { HiCheck } from 'react-icons/hi'
import type { CommonProps } from '@/@types/common' import type { CommonProps } from '@/@types/common'
import appConfig from '@/configs/app.config' import appConfig from '@/configs/app.config'
import { dateLocales } from '@/constants/dateLocales.constant'
const _LanguageSelector = ({ className }: CommonProps) => { const _LanguageSelector = ({ className }: CommonProps) => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
@ -41,7 +39,6 @@ const _LanguageSelector = ({ className }: CommonProps) => {
setLoading(true) setLoading(true)
const dispatchLang = () => { const dispatchLang = () => {
i18n.changeLanguage(cultureName)
setLang(cultureName) setLang(cultureName)
setLoading(false) setLoading(false)
} }

View file

@ -12,28 +12,21 @@ import StackedSideNavSecondary from './StackedSideNavSecondary'
import useResponsive from '@/utils/hooks/useResponsive' import useResponsive from '@/utils/hooks/useResponsive'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import { useStoreState } from '@/store' import { useStoreState } from '@/store'
import { useTranslation } from 'react-i18next'
const stackedSideNavDefaultStyle = { const stackedSideNavDefaultStyle = {
width: SPLITTED_SIDE_NAV_MINI_WIDTH, width: SPLITTED_SIDE_NAV_MINI_WIDTH,
} }
const StackedSideNav = () => { const StackedSideNav = () => {
const { t } = useTranslation()
const [selectedMenu, setSelectedMenu] = useState<SelectedMenuItem>({}) const [selectedMenu, setSelectedMenu] = useState<SelectedMenuItem>({})
const [activeKeys, setActiveKeys] = useState<string[]>([]) const [activeKeys, setActiveKeys] = useState<string[]>([])
const themeColor = useStoreState((state) => state.theme.themeColor) const themeColor = useStoreState((state) => state.theme.themeColor)
const primaryColorLevel = useStoreState( const primaryColorLevel = useStoreState((state) => state.theme.primaryColorLevel)
(state) => state.theme.primaryColorLevel
)
const navMode = useStoreState((state) => state.theme.navMode) const navMode = useStoreState((state) => state.theme.navMode)
const mode = useStoreState((state) => state.theme.mode) const mode = useStoreState((state) => state.theme.mode)
const direction = useStoreState((state) => state.theme.direction) const direction = useStoreState((state) => state.theme.direction)
const currentRouteKey = useStoreState( const currentRouteKey = useStoreState((state) => state.base.common.currentRouteKey)
(state) => state.base.common.currentRouteKey
)
const userAuthority = useStoreState((state) => state.auth.user.authority) const userAuthority = useStoreState((state) => state.auth.user.authority)
const { larger } = useResponsive() const { larger } = useResponsive()
@ -77,10 +70,7 @@ const StackedSideNav = () => {
{larger.md && ( {larger.md && (
<div className={`stacked-side-nav`}> <div className={`stacked-side-nav`}>
<StackedSideNavMini <StackedSideNavMini
className={`stacked-side-nav-mini ${navColor( className={`stacked-side-nav-mini ${navColor('stacked-side-nav-mini', navMode)}`}
'stacked-side-nav-mini',
navMode
)}`}
style={stackedSideNavDefaultStyle} style={stackedSideNavDefaultStyle}
routeKey={currentRouteKey} routeKey={currentRouteKey}
activeKeys={activeKeys} activeKeys={activeKeys}
@ -95,21 +85,16 @@ const StackedSideNav = () => {
className={`stacked-side-nav-secondary ${navColor( className={`stacked-side-nav-secondary ${navColor(
'stacked-side-nav-secondary', 'stacked-side-nav-secondary',
mode, mode,
false false,
)}`} )}`}
style={{ style={{
width: SPLITTED_SIDE_NAV_SECONDARY_WIDTH, width: SPLITTED_SIDE_NAV_SECONDARY_WIDTH,
...(isEmpty(selectedMenu) ...(isEmpty(selectedMenu) ? stackedSideNavSecondaryDirStyle() : {}),
? stackedSideNavSecondaryDirStyle()
: {}),
}} }}
> >
{!isEmpty(selectedMenu) && ( {!isEmpty(selectedMenu) && (
<StackedSideNavSecondary <StackedSideNavSecondary
title={t( title={selectedMenu.title as string}
selectedMenu.translateKey as string,
selectedMenu.title as string
)}
menu={selectedMenu.menu} menu={selectedMenu.menu}
routeKey={currentRouteKey} routeKey={currentRouteKey}
navMode={NAV_MODE_TRANSPARENT} navMode={NAV_MODE_TRANSPARENT}

View file

@ -4,7 +4,6 @@ import type { Direction } from '@/@types/theme'
import { PermissionCheck } from '@/components/shared' import { PermissionCheck } from '@/components/shared'
import Dropdown from '@/components/ui/Dropdown' import Dropdown from '@/components/ui/Dropdown'
import Menu from '@/components/ui/Menu' import Menu from '@/components/ui/Menu'
import { Trans } from 'react-i18next'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import VerticalMenuIcon from './VerticalMenuIcon' import VerticalMenuIcon from './VerticalMenuIcon'
@ -33,9 +32,7 @@ const DefaultItem = ({ nav, onLinkClick }: DefaultItemProps) => {
label={ label={
<> <>
<VerticalMenuIcon icon={nav.icon} /> <VerticalMenuIcon icon={nav.icon} />
<span> <span>{nav.title}</span>
<Trans i18nKey={nav.translateKey} defaults={nav.title} />
</span>
</> </>
} }
eventKey={nav.key} eventKey={nav.key}
@ -58,14 +55,10 @@ const DefaultItem = ({ nav, onLinkClick }: DefaultItemProps) => {
} }
target={subNav.isExternalLink ? '_blank' : ''} target={subNav.isExternalLink ? '_blank' : ''}
> >
<span> <span>{nav.title}</span>
<Trans i18nKey={subNav.translateKey} defaults={subNav.title} />
</span>
</Link> </Link>
) : ( ) : (
<span> <span>{nav.title}</span>
<Trans i18nKey={subNav.translateKey} defaults={subNav.title} />
</span>
)} )}
</MenuItem> </MenuItem>
</PermissionCheck> </PermissionCheck>
@ -105,14 +98,10 @@ const CollapsedItem = ({ nav, onLinkClick, direction }: CollapsedItemProps) => {
} }
target={subNav.isExternalLink ? '_blank' : ''} target={subNav.isExternalLink ? '_blank' : ''}
> >
<span> <span>{subNav.title}</span>
<Trans i18nKey={subNav.translateKey} defaults={subNav.title} />
</span>
</Link> </Link>
) : ( ) : (
<span> <span>{subNav.title}</span>
<Trans i18nKey={subNav.translateKey} defaults={subNav.title} />
</span>
)} )}
</Dropdown.Item> </Dropdown.Item>
</PermissionCheck> </PermissionCheck>

View file

@ -10,7 +10,6 @@ import {
} from '@/constants/navigation.constant' } from '@/constants/navigation.constant'
import useMenuActive from '@/utils/hooks/useMenuActive' import useMenuActive from '@/utils/hooks/useMenuActive'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import VerticalCollapsedMenuItem from './VerticalCollapsedMenuItem' import VerticalCollapsedMenuItem from './VerticalCollapsedMenuItem'
import VerticalSingleMenuItem from './VerticalSingleMenuItem' import VerticalSingleMenuItem from './VerticalSingleMenuItem'
@ -42,9 +41,6 @@ const VerticalMenuContent = (props: VerticalMenuContentProps) => {
onMenuItemClick, onMenuItemClick,
direction = themeConfig.direction, direction = themeConfig.direction,
} = props } = props
const { t } = useTranslation()
const [defaultExpandedKeys, setDefaultExpandedKeys] = useState<string[]>([]) const [defaultExpandedKeys, setDefaultExpandedKeys] = useState<string[]>([])
const [defaultActiveKeys, setDefaultActiveKeys] = useState<string[]>([]) const [defaultActiveKeys, setDefaultActiveKeys] = useState<string[]>([])
@ -105,9 +101,7 @@ const VerticalMenuContent = (props: VerticalMenuContentProps) => {
label={ label={
<> <>
<VerticalMenuIcon icon={nav.icon} /> <VerticalMenuIcon icon={nav.icon} />
<span> <span>{nav.title}</span>
<Trans i18nKey={nav.translateKey} defaults={nav.title} />
</span>
</> </>
} }
eventKey={nav.key} eventKey={nav.key}

View file

@ -2,7 +2,6 @@ import Tooltip from '@/components/ui/Tooltip'
import Menu from '@/components/ui/Menu' import Menu from '@/components/ui/Menu'
import VerticalMenuIcon from './VerticalMenuIcon' import VerticalMenuIcon from './VerticalMenuIcon'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Trans, useTranslation } from 'react-i18next'
import type { CommonProps } from '@/@types/common' import type { CommonProps } from '@/@types/common'
import type { Direction } from '@/@types/theme' import type { Direction } from '@/@types/theme'
import type { NavigationTree } from '@/@types/navigation' import type { NavigationTree } from '@/@types/navigation'
@ -27,10 +26,8 @@ interface DefaultItemProps {
interface VerticalMenuItemProps extends CollapsedItemProps, DefaultItemProps {} interface VerticalMenuItemProps extends CollapsedItemProps, DefaultItemProps {}
const CollapsedItem = ({ title, translateKey, children, direction }: CollapsedItemProps) => { const CollapsedItem = ({ title, translateKey, children, direction }: CollapsedItemProps) => {
const { t } = useTranslation()
return ( return (
<Tooltip title={t(translateKey) || title} placement={direction === 'rtl' ? 'left' : 'right'}> <Tooltip title={title} placement={direction === 'rtl' ? 'left' : 'right'}>
{children} {children}
</Tooltip> </Tooltip>
) )
@ -55,11 +52,7 @@ const DefaultItem = (props: DefaultItemProps) => {
target={nav.isExternalLink ? '_blank' : ''} target={nav.isExternalLink ? '_blank' : ''}
> >
<VerticalMenuIcon icon={nav.icon} /> <VerticalMenuIcon icon={nav.icon} />
{!sideCollapsed && ( {!sideCollapsed && <span>{nav.title}</span>}
<span>
<Trans i18nKey={nav.translateKey} defaults={nav.title} />
</span>
)}
</Link> </Link>
</MenuItem> </MenuItem>
</PermissionCheck> </PermissionCheck>

View file

@ -1,24 +1,3 @@
// eslint-disable-next-line import/no-named-as-default
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import en from './lang/en.json'
import appConfig from '@/configs/app.config'
const resources = {
en: {
translation: en,
},
}
i18n.use(initReactI18next).init({
resources,
fallbackLng: appConfig.locale,
lng: appConfig.locale,
interpolation: {
escapeValue: false,
},
})
export const dateLocales: { export const dateLocales: {
[key: string]: () => Promise<ILocale> [key: string]: () => Promise<ILocale>
} = { } = {
@ -40,5 +19,3 @@ export const dateLocales: {
tr: () => import('dayjs/locale/tr'), tr: () => import('dayjs/locale/tr'),
zh: () => import('dayjs/locale/zh'), zh: () => import('dayjs/locale/zh'),
} }
export default i18n

View file

@ -1,4 +0,0 @@
import i18n from './locales'
export { dateLocales } from './locales'
export default i18n

View file

@ -1,20 +0,0 @@
{
"nav": {
"home": "Home",
"singleMenuItem": "Single menu item",
"collapseMenu": {
"collapseMenu": "Collapse menu item",
"item1": "Collapse menu item 1",
"item2": "Collapse menu item 2"
},
"groupMenu": {
"groupMenu": "Group menu",
"single": "Group single menu item",
"collapse": {
"collapse": "Group collapse menu",
"item1": "Group collapse menu item 1",
"item2": "Group collapse menu item 2"
}
}
}
}

View file

@ -1,5 +1,5 @@
import { AuditedEntityDto } from '@/proxy/abp' import { AuditedEntityDto } from '@/proxy/abp'
import { PermissionCrudDto } from '@/proxy/form' import { PermissionCrudDto } from '../form/models'
export interface BreakDto { export interface BreakDto {
endValue: number endValue: number

View file

@ -1,4 +1,4 @@
import { RouteDto } from '@/proxy/routes' import { RouteDto } from '@/proxy/routes/models'
import { lazy } from 'react' import { lazy } from 'react'
// Tüm view bileşenlerini import et (vite özel) // Tüm view bileşenlerini import et (vite özel)
@ -27,11 +27,11 @@ export function loadComponent(componentPath: string) {
// React Router için uygun bir route tipi // React Router için uygun bir route tipi
export interface DynamicReactRoute { export interface DynamicReactRoute {
key: string; key: string
path: string; path: string
getComponent: () => React.LazyExoticComponent<React.ComponentType<any>>; getComponent: () => React.LazyExoticComponent<React.ComponentType<any>>
routeType: string; routeType: string
authority?: string[]; authority?: string[]
} }
// API'den gelen route objesini, React Router için uygun hale getirir // API'den gelen route objesini, React Router için uygun hale getirir
@ -42,5 +42,5 @@ export function mapDynamicRoutes(routes: RouteDto[]): DynamicReactRoute[] {
getComponent: () => loadComponent(route.componentPath), getComponent: () => loadComponent(route.componentPath),
routeType: route.routeType, routeType: route.routeType,
authority: route.authority, authority: route.authority,
})); }))
} }

View file

@ -1,4 +1,4 @@
import { RouteDto } from '@/proxy/routes' import { RouteDto } from '@/proxy/routes/models'
import React, { createContext, useContext, useEffect, useState } from 'react' import React, { createContext, useContext, useEffect, useState } from 'react'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'

View file

@ -1,7 +1,7 @@
import { AxiosError } from 'axios' import { AxiosError } from 'axios'
import { SignUpCredential, SignUpResponse } from '../@types/auth' import { SignUpCredential, SignUpResponse } from '../@types/auth'
import apiService from './api.service' import apiService from './api.service'
import { ProfileDto, UpdateProfileDto } from '../proxy/account' import { ProfileDto, UpdateProfileDto } from '@/proxy/account/models'
export const register = (data: SignUpCredential) => export const register = (data: SignUpCredential) =>
apiService.fetchData<SignUpResponse>({ apiService.fetchData<SignUpResponse>({

View file

@ -1,6 +1,6 @@
import { ListResultDto } from '../../proxy' import { ListResultDto } from '../../proxy'
import { ListFormCustomizationRequestDto } from '../../proxy/admin/list-form-customization/models' import { ListFormCustomizationRequestDto } from '../../proxy/admin/list-form-customization/models'
import { ListFormCustomizationDto } from '../../proxy/form' import { ListFormCustomizationDto } from '../../proxy/form/models'
import apiService from '../api.service' import apiService from '../api.service'
export const getListFormCustomizations = (input: ListFormCustomizationRequestDto) => export const getListFormCustomizations = (input: ListFormCustomizationRequestDto) =>

View file

@ -1,5 +1,5 @@
import { ListFormJsonRowDto, ListFormWizardDto } from '../../proxy/admin/list-form/models' import { ListFormJsonRowDto, ListFormWizardDto } from '../../proxy/admin/list-form/models'
import { FieldsDefaultValueDto, GridOptionsEditDto } from '../../proxy/form' import { FieldsDefaultValueDto, GridOptionsEditDto } from '../../proxy/form/models'
import apiService from '../api.service' import apiService from '../api.service'
export const postListFormWizard = (input: ListFormWizardDto) => export const postListFormWizard = (input: ListFormWizardDto) =>

View file

@ -1,5 +1,5 @@
import { AiDto } from '@/proxy/ai/models'
import { PagedAndSortedResultRequestDto, PagedResultDto } from '../proxy' import { PagedAndSortedResultRequestDto, PagedResultDto } from '../proxy'
import { AiDto } from '../proxy/ai'
import apiService, { Config } from './api.service' import apiService, { Config } from './api.service'
export class AiService { export class AiService {

View file

@ -1,6 +1,6 @@
import { AxiosError, Method } from 'axios' import { AxiosError, Method } from 'axios'
import { URLSearchParams } from 'url' import { URLSearchParams } from 'url'
import { GridDto } from '../proxy/form' import { GridDto } from '../proxy/form/models'
import apiService from './api.service' import apiService from './api.service'
export const getList = (data: Record<string, string>) => { export const getList = (data: Record<string, string>) => {

View file

@ -1,4 +1,3 @@
import { ListResultDto } from '../proxy'
import { import {
GetPermissionListResultDto, GetPermissionListResultDto,
IdentityRoleDto, IdentityRoleDto,
@ -7,7 +6,8 @@ import {
UpdatePermissionsDto, UpdatePermissionsDto,
UserClaimModel, UserClaimModel,
UserInfoViewModel, UserInfoViewModel,
} from '../proxy/admin' } from '@/proxy/admin/models'
import { ListResultDto } from '../proxy'
import { AuditLogDto } from '../proxy/auditLog/audit-log' import { AuditLogDto } from '../proxy/auditLog/audit-log'
import apiService from './api.service' import apiService from './api.service'

View file

@ -1,4 +1,4 @@
import { GridDto } from '@/proxy/form' import { GridDto } from '@/proxy/form/models'
import { import {
ImportPreviewData, ImportPreviewData,
ListFormImportDto, ListFormImportDto,
@ -177,7 +177,10 @@ ${headers
} }
}) })
await this.updateSession(sessionId, { totalRows: rows.length, listFormCode: gridDto?.gridOptions.listFormCode || '' }) await this.updateSession(sessionId, {
totalRows: rows.length,
listFormCode: gridDto?.gridOptions.listFormCode || '',
})
return { return {
sessionId, sessionId,

View file

@ -2,7 +2,7 @@ import {
ListFormCustomizationDto, ListFormCustomizationDto,
ListFormCustomizationForUserDto, ListFormCustomizationForUserDto,
ListFormCustomizationTypeEnum, ListFormCustomizationTypeEnum,
} from '../proxy/form' } from '../proxy/form/models'
import apiService from './api.service' import apiService from './api.service'
export const getListFormCustomization = ( export const getListFormCustomization = (

View file

@ -1,5 +1,5 @@
import { IdentityRoleDto, IdentityUserDto } from '@/proxy/admin/models'
import { ListResultDto, PagedResultDto } from '../proxy' import { ListResultDto, PagedResultDto } from '../proxy'
import { IdentityRoleDto, IdentityUserDto } from '../proxy/admin'
import { import {
CreateUpdateOrganizationUnitDto, CreateUpdateOrganizationUnitDto,
OrganizationUnitDto, OrganizationUnitDto,

View file

@ -1,4 +1,4 @@
import { RouteDto } from '@/proxy/routes' import { RouteDto } from '@/proxy/routes/models'
import apiService, { Config } from '@/services/api.service' import apiService, { Config } from '@/services/api.service'
export class RouteService { export class RouteService {

View file

@ -1,6 +1,6 @@
import { AxiosError } from 'axios' import { AxiosError } from 'axios'
import { MainGroupedSettingDto } from '../proxy/settings'
import apiService from './api.service' import apiService from './api.service'
import { MainGroupedSettingDto } from '@/proxy/settings/models'
export const getList = () => export const getList = () =>
apiService.fetchData<MainGroupedSettingDto[]>({ apiService.fetchData<MainGroupedSettingDto[]>({

View file

@ -1,35 +1,24 @@
// scripts/tenant.ts // scripts/tenant.ts
import axios from 'axios'
import https from 'https'
export type TenantDto = { name: string } export type TenantDto = { name: string }
export type PagedResultDto<T> = { totalCount: number; items: T[] } export type PagedResultDto<T> = { totalCount: number; items: T[] }
if (process.env.NODE_ENV === 'development') {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
}
export async function fetchTenantNames(apiUrl: string): Promise<string[]> { export async function fetchTenantNames(apiUrl: string): Promise<string[]> {
try { try {
const url = `${apiUrl.replace(/\/$/, '')}/api/app/platform-tenant?skipCount=0&maxResultCount=1000` const url = `${apiUrl.replace(/\/$/, '')}/api/app/platform-tenant?skipCount=0&maxResultCount=1000`
const isLocalHttps = /^https:\/\/localhost(:\d+)?/i.test(apiUrl)
const response = await fetch(url, { const response = await axios.get<PagedResultDto<TenantDto>>(url, {
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
}, },
httpsAgent: isLocalHttps ? new https.Agent({ rejectUnauthorized: false }) : undefined,
}) })
if (!response.ok) { return (response.data.items ?? []).map((t: TenantDto) => t.name.trim().toLowerCase())
console.error('[vite] Tenant API hatası:', response.status, response.statusText)
return []
}
const data = (await response.json()) as PagedResultDto<TenantDto>
return (data.items ?? [])
.map((t) =>
String(t?.name || '')
.trim()
.toLowerCase(),
)
.filter(Boolean)
} catch (e) { } catch (e) {
console.error('[vite] Tenant listesi alınamadı:', e) console.error('[vite] Tenant listesi alınamadı:', e)
return [] return []

View file

@ -1,10 +1,7 @@
import { useEffect } from 'react' import { useEffect } from 'react'
// eslint-disable-next-line import/no-named-as-default
import i18n from 'i18next'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { dateLocales } from '@/locales'
import { useStoreState } from '@/store' import { useStoreState } from '@/store'
import { locale } from 'devextreme/localization' import { dateLocales } from '@/constants/dateLocales.constant'
function useLocale() { function useLocale() {
const cultureName = useStoreState((state) => state.locale.currentLang) const cultureName = useStoreState((state) => state.locale.currentLang)
@ -14,10 +11,6 @@ function useLocale() {
)?.twoLetterISOLanguageName )?.twoLetterISOLanguageName
useEffect(() => { useEffect(() => {
if (cultureName !== i18n.language) {
i18n.changeLanguage(cultureName)
locale(twoLetterISOLanguageName ?? cultureName)
}
if (cultureName && twoLetterISOLanguageName && dateLocales[twoLetterISOLanguageName]) { if (cultureName && twoLetterISOLanguageName && dateLocales[twoLetterISOLanguageName]) {
dateLocales[twoLetterISOLanguageName]().then(() => { dateLocales[twoLetterISOLanguageName]().then(() => {
dayjs.locale(cultureName) dayjs.locale(cultureName)

View file

@ -1,5 +1,10 @@
import { ListFormEditTabs } from '@/proxy/admin/list-form/models' import { ListFormEditTabs } from '@/proxy/admin/list-form/models'
import { CommandColumnDto, EditingFormDto, FieldsDefaultValueDto, SubFormDto } from '@/proxy/form' import {
CommandColumnDto,
EditingFormDto,
FieldsDefaultValueDto,
SubFormDto,
} from '@/proxy/form/models'
import { ExtractNestedValues } from '@/utils/extractNestedValues' import { ExtractNestedValues } from '@/utils/extractNestedValues'
export type JsonRowOperation = 'create' | 'update' | 'delete' export type JsonRowOperation = 'create' | 'update' | 'delete'

View file

@ -6,11 +6,11 @@ import { useEffect, useState } from 'react'
import { MdBarChart, MdList } from 'react-icons/md' import { MdBarChart, MdList } from 'react-icons/md'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import Grid from '../list/Grid' import Grid from '../list/Grid'
import Chart from '../chart/Chart'
import { GridDto, SubFormDto, SubFormTabTypeEnum } from '@/proxy/form/models'
import FormEdit from './FormEdit' import FormEdit from './FormEdit'
import FormNew from './FormNew' import FormNew from './FormNew'
import FormView from './FormView' import FormView from './FormView'
import Chart from '../chart/Chart'
import { GridDto, SubFormDto, SubFormTabTypeEnum } from '@/proxy/form/models'
const SubForms = (props: { const SubForms = (props: {
gridDto: GridDto gridDto: GridDto

View file

@ -1,4 +1,4 @@
import { GridBoxOptionsDto } from '@/proxy/form' import { GridBoxOptionsDto } from '@/proxy/form/models'
import { GridColumnData } from '@/views/list/GridColumnData' import { GridColumnData } from '@/views/list/GridColumnData'
import { Button } from 'devextreme-react/button' import { Button } from 'devextreme-react/button'
import DataGrid from 'devextreme-react/data-grid' import DataGrid from 'devextreme-react/data-grid'

View file

@ -1,4 +1,4 @@
import { TagBoxOptionsDto } from '@/proxy/form' import { TagBoxOptionsDto } from '@/proxy/form/models'
import { GridColumnData } from '@/views/list/GridColumnData' import { GridColumnData } from '@/views/list/GridColumnData'
import TagBox from 'devextreme-react/tag-box' import TagBox from 'devextreme-react/tag-box'
import { ApplyValueMode } from 'devextreme/common' import { ApplyValueMode } from 'devextreme/common'

View file

@ -2,7 +2,7 @@ import { FormItemComponent, SimpleItem } from 'devextreme/ui/form'
import { GridColumnData } from '../list/GridColumnData' import { GridColumnData } from '../list/GridColumnData'
import { Overwrite } from '../../utils/types' import { Overwrite } from '../../utils/types'
import { GridBoxOptionsDto, PlatformEditorTypes, TagBoxOptionsDto } from '../../proxy/form/models' import { GridBoxOptionsDto, PlatformEditorTypes, TagBoxOptionsDto } from '../../proxy/form/models'
import { Meta } from 'easy-peasy' import { Meta } from '@/@types/routes'
export type EditorType2 = FormItemComponent | PlatformEditorTypes.dxGridBox export type EditorType2 = FormItemComponent | PlatformEditorTypes.dxGridBox
export type SimpleItemWithColData = Overwrite< export type SimpleItemWithColData = Overwrite<

View file

@ -402,8 +402,7 @@ export function PostManagement({
setPostToDelete(null) setPostToDelete(null)
} }
}} }}
> ></ConfirmDialog>
</ConfirmDialog>
)} )}
</div> </div>
) )

3
ui/src/views/index.ts Normal file
View file

@ -0,0 +1,3 @@
import Views from './Views'
export default Views