mirror of https://github.com/langgenius/dify.git
feat: add time zone
This commit is contained in:
parent
784a236280
commit
95ce7b6f47
|
|
@ -111,6 +111,15 @@ const TimePicker = ({
|
|||
const displayValue = value?.format(timeFormat) || ''
|
||||
const placeholderDate = isOpen && selectedTime ? selectedTime.format(timeFormat) : (placeholder || t('time.defaultPlaceholder'))
|
||||
|
||||
const inputElem = (
|
||||
<input
|
||||
className='system-xs-regular flex-1 cursor-pointer appearance-none truncate bg-transparent p-1
|
||||
text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder'
|
||||
readOnly
|
||||
value={isOpen ? '' : displayValue}
|
||||
placeholder={placeholderDate}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={isOpen}
|
||||
|
|
@ -118,18 +127,16 @@ const TimePicker = ({
|
|||
placement='bottom-end'
|
||||
>
|
||||
<PortalToFollowElemTrigger>
|
||||
{renderTrigger ? (renderTrigger()) : (
|
||||
{renderTrigger ? (renderTrigger({
|
||||
inputElem,
|
||||
onClick: handleClickTrigger,
|
||||
isOpen,
|
||||
})) : (
|
||||
<div
|
||||
className='group flex w-[252px] cursor-pointer items-center gap-x-0.5 rounded-lg bg-components-input-bg-normal px-2 py-1 hover:bg-state-base-hover-alt'
|
||||
onClick={handleClickTrigger}
|
||||
>
|
||||
<input
|
||||
className='system-xs-regular flex-1 cursor-pointer appearance-none truncate bg-transparent p-1
|
||||
text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder'
|
||||
readOnly
|
||||
value={isOpen ? '' : displayValue}
|
||||
placeholder={placeholderDate}
|
||||
/>
|
||||
{inputElem}
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-quaternary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
|
|
|
|||
|
|
@ -48,13 +48,18 @@ export type DatePickerFooterProps = {
|
|||
handleConfirmDate: () => void
|
||||
}
|
||||
|
||||
export type TriggerParams = {
|
||||
isOpen: boolean
|
||||
inputElem: React.ReactNode
|
||||
onClick: (e: React.MouseEvent) => void
|
||||
}
|
||||
export type TimePickerProps = {
|
||||
value: Dayjs | undefined
|
||||
timezone?: string
|
||||
placeholder?: string
|
||||
onChange: (date: Dayjs | undefined) => void
|
||||
onClear: () => void
|
||||
renderTrigger?: () => React.ReactNode
|
||||
renderTrigger?: (props: TriggerParams) => React.ReactNode
|
||||
title?: string
|
||||
minuteFilter?: (minutes: string[]) => string[]
|
||||
popupClassName?: string
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import dayjs, { type Dayjs } from 'dayjs'
|
|||
import type { Day } from '../types'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import timezone from 'dayjs/plugin/timezone'
|
||||
import tz from '@/utils/timezone.json'
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
|
@ -78,3 +79,14 @@ export const getHourIn12Hour = (date: Dayjs) => {
|
|||
export const getDateWithTimezone = (props: { date?: Dayjs, timezone?: string }) => {
|
||||
return props.date ? dayjs.tz(props.date, props.timezone) : dayjs().tz(props.timezone)
|
||||
}
|
||||
|
||||
// Asia/Shanghai -> UTC+8
|
||||
const DEFAULT_OFFSET_STR = 'UTC+0'
|
||||
export const convertTimezoneToOffsetStr = (timezone?: string) => {
|
||||
if(!timezone)
|
||||
return DEFAULT_OFFSET_STR
|
||||
const tzItem = tz.find(item => item.value === timezone)
|
||||
if(!tzItem)
|
||||
return DEFAULT_OFFSET_STR
|
||||
return `UTC${tzItem.name.charAt(0)}${tzItem.name.charAt(2)}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,17 @@ import React, { useCallback, useMemo } from 'react'
|
|||
import { AUTO_UPDATE_MODE, AUTO_UPDATE_STRATEGY, type AutoUpdateConfig } from './types'
|
||||
import Label from '../label'
|
||||
import StrategyPicker from './strategy-picker'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import TimePicker from '@/app/components/base/date-and-time-picker/time-picker'
|
||||
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||
import PluginsPicker from './plugins-picker'
|
||||
import { convertLocalSecondsToUTCDaySeconds, convertUTCDaySecondsToLocalSeconds, dayjsToTimeOfDay, timeOfDayToDayjs } from './utils'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import type { TriggerParams } from '@/app/components/base/date-and-time-picker/types'
|
||||
import { RiTimeLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs'
|
||||
import { useModalContextSelector } from '@/context/modal-context'
|
||||
|
||||
const i18nPrefix = 'plugin.autoUpdate'
|
||||
|
||||
|
|
@ -18,6 +23,16 @@ type Props = {
|
|||
onChange: (payload: AutoUpdateConfig) => void
|
||||
}
|
||||
|
||||
const SettingTimeZone: FC<{
|
||||
children?: React.ReactNode
|
||||
}> = ({
|
||||
children,
|
||||
}) => {
|
||||
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
|
||||
return (
|
||||
<span className='body-xs-regular cursor-pointer text-text-accent' onClick={() => setShowAccountSettingModal({ payload: 'language' })} >{children}</span>
|
||||
)
|
||||
}
|
||||
const AutoUpdateSetting: FC<Props> = ({
|
||||
payload,
|
||||
onChange,
|
||||
|
|
@ -83,10 +98,29 @@ const AutoUpdateSetting: FC<Props> = ({
|
|||
})
|
||||
}
|
||||
}, [payload, onChange])
|
||||
|
||||
const renderTimePickerTrigger = useCallback(({ inputElem, onClick, isOpen }: TriggerParams) => {
|
||||
return (
|
||||
<div
|
||||
className='group float-right flex h-8 w-[160px] cursor-pointer items-center justify-between rounded-lg bg-components-input-bg-normal px-2 hover:bg-state-base-hover-alt'
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='flex w-0 grow items-center gap-x-1'>
|
||||
<RiTimeLine className={cn(
|
||||
'h-4 w-4 shrink-0 text-text-tertiary',
|
||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||
)} />
|
||||
{inputElem}
|
||||
</div>
|
||||
<div className='system-sm-regular text-text-tertiary'>{convertTimezoneToOffsetStr(timezone)}</div>
|
||||
</div>
|
||||
)
|
||||
}, [timezone])
|
||||
|
||||
return (
|
||||
<div className='self-stretch px-6'>
|
||||
<div className='my-3 flex items-center'>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>Updates Settings</div>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.updateSettings`)}</div>
|
||||
<div className='ml-2 h-px grow bg-divider-subtle'></div>
|
||||
</div>
|
||||
|
||||
|
|
@ -99,15 +133,26 @@ const AutoUpdateSetting: FC<Props> = ({
|
|||
<>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label label={t(`${i18nPrefix}.updateTime`)} />
|
||||
<TimePicker
|
||||
value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
|
||||
timezone={timezone}
|
||||
onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
|
||||
onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
|
||||
popupClassName='z-[99]'
|
||||
title={t(`${i18nPrefix}.updateTime`)}
|
||||
minuteFilter={minuteFilter}
|
||||
/>
|
||||
<div className='flex flex-col justify-start'>
|
||||
<TimePicker
|
||||
value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
|
||||
timezone={timezone}
|
||||
onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
|
||||
onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
|
||||
popupClassName='z-[99]'
|
||||
title={t(`${i18nPrefix}.updateTime`)}
|
||||
minuteFilter={minuteFilter}
|
||||
renderTrigger={renderTimePickerTrigger}
|
||||
/>
|
||||
<div className='body-xs-regular mt-1 text-right text-text-tertiary'>
|
||||
<Trans
|
||||
i18nKey={`${i18nPrefix}.changeTimezone`}
|
||||
components={{
|
||||
setTimezone: <SettingTimeZone />,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label label={t(`${i18nPrefix}.specifyPluginsToUpdate`)} />
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ const translation = {
|
|||
noFound: 'No plugins were found',
|
||||
noInstalled: 'No plugins installed',
|
||||
},
|
||||
updateSettings: 'Update Settings',
|
||||
changeTimezone: 'To change time zone, go to <setTimezone>Settings</setTimezone>',
|
||||
},
|
||||
pluginInfoModal: {
|
||||
title: 'Plugin info',
|
||||
|
|
|
|||
|
|
@ -160,6 +160,8 @@ const translation = {
|
|||
noFound: '未找到插件',
|
||||
noInstalled: '未安装插件',
|
||||
},
|
||||
updateSettings: '更新设置',
|
||||
changeTimezone: '要更改时区,请前往<setTimezone>设置</setTimezone>',
|
||||
},
|
||||
pluginInfoModal: {
|
||||
title: '插件信息',
|
||||
|
|
|
|||
Loading…
Reference in New Issue