123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import {
- useMemo,
- useState,
- } from 'react'
- import {
- RiCheckboxCircleFill,
- RiErrorWarningFill,
- RiInstallLine,
- } from '@remixicon/react'
- import { useTranslation } from 'react-i18next'
- import { usePluginTaskStatus } from './hooks'
- import {
- PortalToFollowElem,
- PortalToFollowElemContent,
- PortalToFollowElemTrigger,
- } from '@/app/components/base/portal-to-follow-elem'
- import Tooltip from '@/app/components/base/tooltip'
- import Button from '@/app/components/base/button'
- import ProgressCircle from '@/app/components/base/progress-bar/progress-circle'
- import CardIcon from '@/app/components/plugins/card/base/card-icon'
- import cn from '@/utils/classnames'
- import { useGetLanguage } from '@/context/i18n'
- import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
- import DownloadingIcon from '@/app/components/header/plugins-nav/downloading-icon'
- const PluginTasks = () => {
- const { t } = useTranslation()
- const language = useGetLanguage()
- const [open, setOpen] = useState(false)
- const {
- errorPlugins,
- runningPluginsLength,
- successPluginsLength,
- errorPluginsLength,
- totalPluginsLength,
- isInstalling,
- isInstallingWithSuccess,
- isInstallingWithError,
- isSuccess,
- isFailed,
- handleClearErrorPlugin,
- handleClearAllErrorPlugin,
- opacity,
- } = usePluginTaskStatus()
- const { getIconUrl } = useGetIcon()
- const tip = useMemo(() => {
- if (isInstalling)
- return t('plugin.task.installing', { installingLength: runningPluginsLength })
- if (isInstallingWithSuccess)
- return t('plugin.task.installingWithSuccess', { installingLength: runningPluginsLength, successLength: successPluginsLength })
- if (isInstallingWithError)
- return t('plugin.task.installingWithError', { installingLength: runningPluginsLength, successLength: successPluginsLength, errorLength: errorPluginsLength })
- if (isFailed)
- return t('plugin.task.installError', { errorLength: errorPluginsLength })
- }, [isInstalling, isInstallingWithSuccess, isInstallingWithError, isFailed, errorPluginsLength, runningPluginsLength, successPluginsLength, t])
- if (!totalPluginsLength)
- return null
- return (
- <div
- className='flex items-center'
- style={{ opacity }}
- >
- <PortalToFollowElem
- open={open}
- onOpenChange={setOpen}
- placement='bottom-start'
- offset={{
- mainAxis: 4,
- crossAxis: 79,
- }}
- >
- <PortalToFollowElemTrigger
- onClick={() => {
- if (isFailed)
- setOpen(v => !v)
- }}
- >
- <Tooltip popupContent={tip}>
- <div
- className={cn(
- 'relative flex h-8 w-8 items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs hover:bg-components-button-secondary-bg-hover',
- (isInstallingWithError || isFailed) && 'cursor-pointer border-components-button-destructive-secondary-border-hover bg-state-destructive-hover hover:bg-state-destructive-hover-alt',
- )}
- id="plugin-task-trigger"
- >
- {
- (isInstalling || isInstallingWithError) && (
- <DownloadingIcon />
- )
- }
- {
- !(isInstalling || isInstallingWithError) && (
- <RiInstallLine
- className={cn(
- 'h-4 w-4 text-components-button-secondary-text',
- (isInstallingWithError || isFailed) && 'text-components-button-destructive-secondary-text',
- )}
- />
- )
- }
- <div className='absolute -right-1 -top-1'>
- {
- (isInstalling || isInstallingWithSuccess) && (
- <ProgressCircle
- percentage={successPluginsLength / totalPluginsLength * 100}
- circleFillColor='fill-components-progress-brand-bg'
- />
- )
- }
- {
- isInstallingWithError && (
- <ProgressCircle
- percentage={runningPluginsLength / totalPluginsLength * 100}
- circleFillColor='fill-components-progress-brand-bg'
- sectorFillColor='fill-components-progress-error-border'
- circleStrokeColor='stroke-components-progress-error-border'
- />
- )
- }
- {
- isSuccess && (
- <RiCheckboxCircleFill className='h-3.5 w-3.5 text-text-success' />
- )
- }
- {
- isFailed && (
- <RiErrorWarningFill className='h-3.5 w-3.5 text-text-destructive' />
- )
- }
- </div>
- </div>
- </Tooltip>
- </PortalToFollowElemTrigger>
- <PortalToFollowElemContent className='z-[11]'>
- <div className='w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 pb-2 shadow-lg'>
- <div className='system-sm-semibold-uppercase sticky top-0 flex h-7 items-center justify-between px-2 pt-1'>
- {t('plugin.task.installedError', { errorLength: errorPluginsLength })}
- <Button
- className='shrink-0'
- size='small'
- variant='ghost'
- onClick={() => handleClearAllErrorPlugin()}
- >
- {t('plugin.task.clearAll')}
- </Button>
- </div>
- <div className='max-h-[400px] overflow-y-auto'>
- {
- errorPlugins.map(errorPlugin => (
- <div
- key={errorPlugin.plugin_unique_identifier}
- className='flex rounded-lg p-2 hover:bg-state-base-hover'
- >
- <div className='relative mr-2 flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
- <RiErrorWarningFill className='absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 text-text-destructive' />
- <CardIcon
- size='tiny'
- src={getIconUrl(errorPlugin.icon)}
- />
- </div>
- <div className='grow'>
- <div className='system-md-regular truncate text-text-secondary'>
- {errorPlugin.labels[language]}
- </div>
- <div className='system-xs-regular break-all text-text-destructive'>
- {errorPlugin.message}
- </div>
- </div>
- <Button
- className='shrink-0'
- size='small'
- variant='ghost'
- onClick={() => handleClearErrorPlugin(errorPlugin.taskId, errorPlugin.plugin_unique_identifier)}
- >
- {t('common.operation.clear')}
- </Button>
- </div>
- ))
- }
- </div>
- </div>
- </PortalToFollowElemContent>
- </PortalToFollowElem>
- </div>
- )
- }
- export default PluginTasks
|