Check New Version eklendi

This commit is contained in:
Sedat ÖZTÜRK 2026-07-14 16:43:05 +03:00
parent 28ec641275
commit 4805e8c4d9
9 changed files with 92 additions and 52 deletions

View file

@ -6,22 +6,31 @@ import withHeaderItem from '@/utils/hoc/withHeaderItem'
import useAuth from '@/utils/hooks/useAuth' import useAuth from '@/utils/hooks/useAuth'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import classNames from 'classnames' import classNames from 'classnames'
import type { ReactElement } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Avatar } from '../ui' import { Avatar } from '../ui'
import { FcEngineering, FcNext, FcNfcSign, FcVoicePresentation } from 'react-icons/fc' import {
FcEngineering,
FcNext,
FcNfcSign,
FcSynchronize,
FcVoicePresentation,
} from 'react-icons/fc'
import { useManualUpdateCheck } from '@/views/version/ChangeLog'
type DropdownList = { type DropdownList = {
label: string label: string
path: string path: string
icon: JSX.Element icon: ReactElement
} }
const _UserDropdown = ({ className }: CommonProps) => { const UserDropdownBase = ({ className }: CommonProps) => {
const { setPanelExpand } = useStoreActions((actions) => actions.theme) const { setPanelExpand } = useStoreActions((actions) => actions.theme)
const { userName, name, email, avatar } = useStoreState((state) => state.auth.user) const { userName, name, email, avatar } = useStoreState((state) => state.auth.user)
const tenant = useStoreState((state) => state.abpConfig.config?.currentTenant) const tenant = useStoreState((state) => state.abpConfig.config?.currentTenant)
const { translate } = useLocalization() const { translate } = useLocalization()
const { signOut } = useAuth() const { signOut } = useAuth()
const { isCheckingUpdate, checkForUpdate } = useManualUpdateCheck()
const displayName = name || userName const displayName = name || userName
const tenantName = tenant?.name const tenantName = tenant?.name
@ -113,6 +122,23 @@ const _UserDropdown = ({ className }: CommonProps) => {
) )
})} })}
<Dropdown.Item
eventKey="Check New Version"
className="mb-1 px-0"
onClick={checkForUpdate}
>
<span className="flex h-full w-full items-center gap-2 px-2">
<span className={classNames('text-xl opacity-50', isCheckingUpdate && 'animate-spin')}>
<FcSynchronize />
</span>
<span>
{isCheckingUpdate
? translate('::App.CheckUpdate')
: translate('::App.CheckForUpdates')}
</span>
</span>
</Dropdown.Item>
{/* <Dropdown.Item variant="divider" /> */} {/* <Dropdown.Item variant="divider" /> */}
<Dropdown.Item eventKey="Sign Out" className="mb-1 px-2" onClick={signOut}> <Dropdown.Item eventKey="Sign Out" className="mb-1 px-2" onClick={signOut}>
<span className="text-xl opacity-50"> <span className="text-xl opacity-50">
@ -124,6 +150,6 @@ const _UserDropdown = ({ className }: CommonProps) => {
) )
} }
const UserDropdown = withHeaderItem(_UserDropdown) const UserDropdown = withHeaderItem(UserDropdownBase)
export default UserDropdown export default UserDropdown

View file

@ -1,10 +1,10 @@
import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa'; import { FaCheckCircle, FaInfoCircle, FaExclamation, FaTimesCircle } from 'react-icons/fa';
import type { TypeAttributes, CommonProps } from '../@types/common' import type { TypeAttributes, CommonProps } from '../@types/common'
import type { ReactNode } from 'react' import type { ReactElement, ReactNode } from 'react'
export interface StatusIconProps extends CommonProps { export interface StatusIconProps extends CommonProps {
type: TypeAttributes.Status type: TypeAttributes.Status
custom?: ReactNode | JSX.Element custom?: ReactNode | ReactElement
iconColor?: string iconColor?: string
} }
@ -12,7 +12,7 @@ const ICONS: Record<
TypeAttributes.Status, TypeAttributes.Status,
{ {
color: string color: string
icon: JSX.Element icon: ReactElement
} }
> = { > = {
success: { success: {

View file

@ -1,11 +1,11 @@
import { LayoutType } from '../theme/models' import { LayoutType } from '../theme/models'
import type { LazyExoticComponent, ReactNode } from 'react' import type { LazyExoticComponent, ReactElement, ReactNode } from 'react'
export interface Meta { export interface Meta {
pageContainerType?: 'default' | 'gutterless' | 'contained' pageContainerType?: 'default' | 'gutterless' | 'contained'
header?: string | ReactNode header?: string | ReactNode
headerContainer?: boolean headerContainer?: boolean
extraHeader?: LazyExoticComponent<() => JSX.Element> extraHeader?: LazyExoticComponent<() => ReactElement>
footer?: boolean footer?: boolean
layout?: LayoutType layout?: LayoutType
} }
@ -13,7 +13,7 @@ export interface Meta {
export type Route = { export type Route = {
key: string key: string
path: string path: string
component: LazyExoticComponent<<T extends Meta>(props: T) => JSX.Element> component: LazyExoticComponent<<T extends Meta>(props: T) => ReactElement>
authority: string[] authority: string[]
meta?: Meta meta?: Meta
} }

View file

@ -45,6 +45,7 @@ export const store = createStore<StoreModel>(
}, },
{ {
allow: ['auth', 'theme', 'locale', 'client', 'admin'], allow: ['auth', 'theme', 'locale', 'client', 'admin'],
storage: 'localStorage',
}, },
), ),
{ {

View file

@ -3,6 +3,7 @@ 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 navigationIcon from '@/proxy/menus/navigation-icon.config'
import { navigationTreeToFlat } from '@/utils/navigation' import { navigationTreeToFlat } from '@/utils/navigation'
import { ReactElement } from 'react'
const extractListFormCode = (path: string): string | null => { const extractListFormCode = (path: string): string | null => {
const p = (path ?? '').toLowerCase() const p = (path ?? '').toLowerCase()
@ -14,7 +15,7 @@ const extractListFormCode = (path: string): string | null => {
return null return null
} }
export function useCurrentMenuIcon(className = 'w-6 h-6'): JSX.Element { 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 menus = navigationTreeToFlat(mainMenu) const menus = navigationTreeToFlat(mainMenu)

View file

@ -1073,6 +1073,7 @@ const useListFormColumns = ({
dataField: item.dataField, dataField: item.dataField,
visible: false, visible: false,
showInColumnChooser: false, showInColumnChooser: false,
allowSearch: false
}) })
insertedColumns.push(item.dataField) insertedColumns.push(item.dataField)
} }

View file

@ -6,8 +6,9 @@ import AuditLogDetail from '@/views/admin/auditLog/AuditLogDetail'
import RolesPermission from '@/views/admin/role-management/RolesPermission' import RolesPermission from '@/views/admin/role-management/RolesPermission'
import UsersPermission from '@/views/admin/user-management/UsersPermission' import UsersPermission from '@/views/admin/user-management/UsersPermission'
import BranchSeed from '@/views/branch/BranchSeed' import BranchSeed from '@/views/branch/BranchSeed'
import { ReactElement } from 'react'
const DialogShowComponent = (): JSX.Element => { const DialogShowComponent = (): ReactElement => {
const dialogContext: any = useDialogContext() const dialogContext: any = useDialogContext()
const handleDialogClose = () => { const handleDialogClose = () => {

View file

@ -4,12 +4,13 @@ import Tr from '@/components/ui/Table/Tr'
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 classNames from 'classnames' import classNames from 'classnames'
import { ReactElement } from 'react'
interface TableNoRecordsProps extends CommonProps { interface TableNoRecordsProps extends CommonProps {
show: boolean show: boolean
} }
const TableNoRecords = (props: TableNoRecordsProps): JSX.Element => { const TableNoRecords = (props: TableNoRecordsProps): ReactElement => {
const { show, children, className, ...rest } = props const { show, children, className, ...rest } = props
const { textTheme } = useThemeClass() const { textTheme } = useThemeClass()

View file

@ -14,47 +14,13 @@ type Release = {
changeLog: string[] changeLog: string[]
} }
const Log = ({ // User dropdown reuses the exact update-check flow owned by this component.
version, // eslint-disable-next-line react-refresh/only-export-components
date, export const useManualUpdateCheck = () => {
children,
}: {
version: string
date: string
children?: React.ReactNode
}) => {
return (
<div className="relative pl-4 sm:pl-32 py-4 group">
<div className="flex flex-col sm:flex-row items-start mb-1 group-last:before:hidden before:absolute before:left-2 sm:before:left-0 before:h-full before:px-px before:bg-slate-200 sm:before:ml-[6.5rem] before:self-start before:-translate-x-1/2 before:translate-y-3 after:absolute after:left-2 sm:after:left-0 after:w-2 after:h-2 after:bg-indigo-600 after:border-4 after:box-content after:border-slate-50 after:rounded-full sm:after:ml-[6.5rem] after:-translate-x-1/2 after:translate-y-1.5">
<time className="sm:absolute left-0 translate-y-0.5 inline-flex items-center justify-center text-xs font-semibold uppercase w-20 h-6 mb-3 sm:mb-0 text-emerald-600 bg-emerald-100 rounded-full">
{date}
</time>
<div className="flex items-center text-xl font-bold text-gray-900 dark:text-white">
<FaTag className="mr-2 text-indigo-500" />v{version}
</div>
</div>
<div className="text-slate-500">{children}</div>
</div>
)
}
const Changelog = () => {
const { translate } = useLocalization() const { translate } = useLocalization()
const [releases, setReleases] = useState<Release[]>([])
const [isCheckingUpdate, setIsCheckingUpdate] = useState(false) const [isCheckingUpdate, setIsCheckingUpdate] = useState(false)
useEffect(() => { const checkForUpdate = async () => {
fetch('/version.json?ts=' + Date.now())
.then((res) => res.json())
.then((data) => {
if (data?.releases) {
setReleases(data.releases)
}
})
.catch(() => setReleases([]))
}, [])
const handleManualUpdate = async () => {
if (isCheckingUpdate) return if (isCheckingUpdate) return
setIsCheckingUpdate(true) setIsCheckingUpdate(true)
@ -96,6 +62,49 @@ const Changelog = () => {
} }
} }
return { isCheckingUpdate, checkForUpdate }
}
const Log = ({
version,
date,
children,
}: {
version: string
date: string
children?: React.ReactNode
}) => {
return (
<div className="relative pl-4 sm:pl-32 py-4 group">
<div className="flex flex-col sm:flex-row items-start mb-1 group-last:before:hidden before:absolute before:left-2 sm:before:left-0 before:h-full before:px-px before:bg-slate-200 sm:before:ml-[6.5rem] before:self-start before:-translate-x-1/2 before:translate-y-3 after:absolute after:left-2 sm:after:left-0 after:w-2 after:h-2 after:bg-indigo-600 after:border-4 after:box-content after:border-slate-50 after:rounded-full sm:after:ml-[6.5rem] after:-translate-x-1/2 after:translate-y-1.5">
<time className="sm:absolute left-0 translate-y-0.5 inline-flex items-center justify-center text-xs font-semibold uppercase w-20 h-6 mb-3 sm:mb-0 text-emerald-600 bg-emerald-100 rounded-full">
{date}
</time>
<div className="flex items-center text-xl font-bold text-gray-900 dark:text-white">
<FaTag className="mr-2 text-indigo-500" />v{version}
</div>
</div>
<div className="text-slate-500">{children}</div>
</div>
)
}
const Changelog = () => {
const { translate } = useLocalization()
const [releases, setReleases] = useState<Release[]>([])
const { isCheckingUpdate, checkForUpdate } = useManualUpdateCheck()
useEffect(() => {
fetch('/version.json?ts=' + Date.now())
.then((res) => res.json())
.then((data) => {
if (data?.releases) {
setReleases(data.releases)
}
})
.catch(() => setReleases([]))
}, [])
return ( return (
<Container> <Container>
<Helmet <Helmet
@ -114,7 +123,7 @@ const Changelog = () => {
icon={<FaSyncAlt />} icon={<FaSyncAlt />}
loading={isCheckingUpdate} loading={isCheckingUpdate}
variant="solid" variant="solid"
onClick={handleManualUpdate} onClick={checkForUpdate}
> >
{isCheckingUpdate {isCheckingUpdate
? translate('::App.CheckUpdate') ? translate('::App.CheckUpdate')