Check New Version eklendi
This commit is contained in:
parent
28ec641275
commit
4805e8c4d9
9 changed files with 92 additions and 52 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ export const store = createStore<StoreModel>(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allow: ['auth', 'theme', 'locale', 'client', 'admin'],
|
allow: ['auth', 'theme', 'locale', 'client', 'admin'],
|
||||||
|
storage: 'localStorage',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = () => {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue