mirror of
https://github.com/langgenius/dify.git
synced 2026-04-26 10:16:40 +08:00
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 displayValue = value?.format(timeFormat) || ''
|
||||||
const placeholderDate = isOpen && selectedTime ? selectedTime.format(timeFormat) : (placeholder || t('time.defaultPlaceholder'))
|
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 (
|
return (
|
||||||
<PortalToFollowElem
|
<PortalToFollowElem
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
@ -118,18 +127,16 @@ const TimePicker = ({
|
|||||||
placement='bottom-end'
|
placement='bottom-end'
|
||||||
>
|
>
|
||||||
<PortalToFollowElemTrigger>
|
<PortalToFollowElemTrigger>
|
||||||
{renderTrigger ? (renderTrigger()) : (
|
{renderTrigger ? (renderTrigger({
|
||||||
|
inputElem,
|
||||||
|
onClick: handleClickTrigger,
|
||||||
|
isOpen,
|
||||||
|
})) : (
|
||||||
<div
|
<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'
|
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}
|
onClick={handleClickTrigger}
|
||||||
>
|
>
|
||||||
<input
|
{inputElem}
|
||||||
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}
|
|
||||||
/>
|
|
||||||
<RiTimeLine className={cn(
|
<RiTimeLine className={cn(
|
||||||
'h-4 w-4 shrink-0 text-text-quaternary',
|
'h-4 w-4 shrink-0 text-text-quaternary',
|
||||||
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
isOpen ? 'text-text-secondary' : 'group-hover:text-text-secondary',
|
||||||
|
|||||||
@ -48,13 +48,18 @@ export type DatePickerFooterProps = {
|
|||||||
handleConfirmDate: () => void
|
handleConfirmDate: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TriggerParams = {
|
||||||
|
isOpen: boolean
|
||||||
|
inputElem: React.ReactNode
|
||||||
|
onClick: (e: React.MouseEvent) => void
|
||||||
|
}
|
||||||
export type TimePickerProps = {
|
export type TimePickerProps = {
|
||||||
value: Dayjs | undefined
|
value: Dayjs | undefined
|
||||||
timezone?: string
|
timezone?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
onChange: (date: Dayjs | undefined) => void
|
onChange: (date: Dayjs | undefined) => void
|
||||||
onClear: () => void
|
onClear: () => void
|
||||||
renderTrigger?: () => React.ReactNode
|
renderTrigger?: (props: TriggerParams) => React.ReactNode
|
||||||
title?: string
|
title?: string
|
||||||
minuteFilter?: (minutes: string[]) => string[]
|
minuteFilter?: (minutes: string[]) => string[]
|
||||||
popupClassName?: string
|
popupClassName?: string
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import dayjs, { type Dayjs } from 'dayjs'
|
|||||||
import type { Day } from '../types'
|
import type { Day } from '../types'
|
||||||
import utc from 'dayjs/plugin/utc'
|
import utc from 'dayjs/plugin/utc'
|
||||||
import timezone from 'dayjs/plugin/timezone'
|
import timezone from 'dayjs/plugin/timezone'
|
||||||
|
import tz from '@/utils/timezone.json'
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
@ -78,3 +79,14 @@ export const getHourIn12Hour = (date: Dayjs) => {
|
|||||||
export const getDateWithTimezone = (props: { date?: Dayjs, timezone?: string }) => {
|
export const getDateWithTimezone = (props: { date?: Dayjs, timezone?: string }) => {
|
||||||
return props.date ? dayjs.tz(props.date, props.timezone) : dayjs().tz(props.timezone)
|
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 { AUTO_UPDATE_MODE, AUTO_UPDATE_STRATEGY, type AutoUpdateConfig } from './types'
|
||||||
import Label from '../label'
|
import Label from '../label'
|
||||||
import StrategyPicker from './strategy-picker'
|
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 TimePicker from '@/app/components/base/date-and-time-picker/time-picker'
|
||||||
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||||
import PluginsPicker from './plugins-picker'
|
import PluginsPicker from './plugins-picker'
|
||||||
import { convertLocalSecondsToUTCDaySeconds, convertUTCDaySecondsToLocalSeconds, dayjsToTimeOfDay, timeOfDayToDayjs } from './utils'
|
import { convertLocalSecondsToUTCDaySeconds, convertUTCDaySecondsToLocalSeconds, dayjsToTimeOfDay, timeOfDayToDayjs } from './utils'
|
||||||
import { useAppContext } from '@/context/app-context'
|
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'
|
const i18nPrefix = 'plugin.autoUpdate'
|
||||||
|
|
||||||
@ -18,6 +23,16 @@ type Props = {
|
|||||||
onChange: (payload: AutoUpdateConfig) => void
|
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> = ({
|
const AutoUpdateSetting: FC<Props> = ({
|
||||||
payload,
|
payload,
|
||||||
onChange,
|
onChange,
|
||||||
@ -83,10 +98,29 @@ const AutoUpdateSetting: FC<Props> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [payload, onChange])
|
}, [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 (
|
return (
|
||||||
<div className='self-stretch px-6'>
|
<div className='self-stretch px-6'>
|
||||||
<div className='my-3 flex items-center'>
|
<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 className='ml-2 h-px grow bg-divider-subtle'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -99,15 +133,26 @@ const AutoUpdateSetting: FC<Props> = ({
|
|||||||
<>
|
<>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<Label label={t(`${i18nPrefix}.updateTime`)} />
|
<Label label={t(`${i18nPrefix}.updateTime`)} />
|
||||||
<TimePicker
|
<div className='flex flex-col justify-start'>
|
||||||
value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
|
<TimePicker
|
||||||
timezone={timezone}
|
value={timeOfDayToDayjs(convertUTCDaySecondsToLocalSeconds(upgrade_time_of_day, timezone!))}
|
||||||
onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
|
timezone={timezone}
|
||||||
onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
|
onChange={v => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(dayjsToTimeOfDay(v), timezone!))}
|
||||||
popupClassName='z-[99]'
|
onClear={() => handleChange('upgrade_time_of_day')(convertLocalSecondsToUTCDaySeconds(0, timezone!))}
|
||||||
title={t(`${i18nPrefix}.updateTime`)}
|
popupClassName='z-[99]'
|
||||||
minuteFilter={minuteFilter}
|
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>
|
||||||
<div>
|
<div>
|
||||||
<Label label={t(`${i18nPrefix}.specifyPluginsToUpdate`)} />
|
<Label label={t(`${i18nPrefix}.specifyPluginsToUpdate`)} />
|
||||||
|
|||||||
@ -160,6 +160,8 @@ const translation = {
|
|||||||
noFound: 'No plugins were found',
|
noFound: 'No plugins were found',
|
||||||
noInstalled: 'No plugins installed',
|
noInstalled: 'No plugins installed',
|
||||||
},
|
},
|
||||||
|
updateSettings: 'Update Settings',
|
||||||
|
changeTimezone: 'To change time zone, go to <setTimezone>Settings</setTimezone>',
|
||||||
},
|
},
|
||||||
pluginInfoModal: {
|
pluginInfoModal: {
|
||||||
title: 'Plugin info',
|
title: 'Plugin info',
|
||||||
|
|||||||
@ -160,6 +160,8 @@ const translation = {
|
|||||||
noFound: '未找到插件',
|
noFound: '未找到插件',
|
||||||
noInstalled: '未安装插件',
|
noInstalled: '未安装插件',
|
||||||
},
|
},
|
||||||
|
updateSettings: '更新设置',
|
||||||
|
changeTimezone: '要更改时区,请前往<setTimezone>设置</setTimezone>',
|
||||||
},
|
},
|
||||||
pluginInfoModal: {
|
pluginInfoModal: {
|
||||||
title: '插件信息',
|
title: '插件信息',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user