mirror of
https://github.com/langgenius/dify.git
synced 2026-08-03 11:46:37 +08:00
Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
207 lines
7.5 KiB
TypeScript
207 lines
7.5 KiB
TypeScript
'use client'
|
|
import type { Reducer } from 'react'
|
|
import type { LanguagesSupported } from '@/i18n-config/language'
|
|
import { Button } from '@langgenius/dify-ui/button'
|
|
import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger } from '@langgenius/dify-ui/select'
|
|
import { toast } from '@langgenius/dify-ui/toast'
|
|
import { useReducer } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import Tooltip from '@/app/components/base/tooltip'
|
|
import { LICENSE_LINK } from '@/constants/link'
|
|
import { languages } from '@/i18n-config/language'
|
|
import Link from '@/next/link'
|
|
import { useRouter, useSearchParams } from '@/next/navigation'
|
|
import { useOneMoreStep } from '@/service/use-common'
|
|
import { timezones } from '@/utils/timezone'
|
|
import Input from '../components/base/input'
|
|
|
|
type IState = {
|
|
invitation_code: string
|
|
interface_language: string
|
|
timezone: string
|
|
}
|
|
|
|
type IAction
|
|
= | { type: 'failed', payload: null }
|
|
| { type: 'invitation_code', value: string }
|
|
| { type: 'interface_language', value: string }
|
|
| { type: 'timezone', value: string }
|
|
|
|
const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
|
|
switch (action.type) {
|
|
case 'invitation_code':
|
|
return { ...state, invitation_code: action.value }
|
|
case 'interface_language':
|
|
return { ...state, interface_language: action.value }
|
|
case 'timezone':
|
|
return { ...state, timezone: action.value }
|
|
case 'failed':
|
|
return {
|
|
invitation_code: '',
|
|
interface_language: 'en-US',
|
|
timezone: 'Asia/Shanghai',
|
|
}
|
|
default:
|
|
throw new Error('Unknown action.')
|
|
}
|
|
}
|
|
|
|
type SelectOption = {
|
|
value: string
|
|
name: string
|
|
}
|
|
|
|
const OneMoreStep = () => {
|
|
const { t } = useTranslation()
|
|
const router = useRouter()
|
|
const searchParams = useSearchParams()
|
|
|
|
const [state, dispatch] = useReducer(reducer, {
|
|
invitation_code: searchParams.get('invitation_code') || '',
|
|
interface_language: 'en-US',
|
|
timezone: 'Asia/Shanghai',
|
|
})
|
|
const { mutateAsync: submitOneMoreStep, isPending } = useOneMoreStep()
|
|
const languageOptions: SelectOption[] = languages.filter(item => item.supported)
|
|
const selectedLanguage = languageOptions.find(item => item.value === state.interface_language)
|
|
const selectedTimezone = timezones.find(item => item.value === state.timezone)
|
|
|
|
const handleSubmit = async () => {
|
|
if (isPending)
|
|
return
|
|
try {
|
|
await submitOneMoreStep({
|
|
invitation_code: state.invitation_code,
|
|
interface_language: state.interface_language,
|
|
timezone: state.timezone,
|
|
})
|
|
router.push('/apps')
|
|
}
|
|
catch (error: any) {
|
|
if (error && error.status === 400)
|
|
toast.error(t('invalidInvitationCode', { ns: 'login' }))
|
|
dispatch({ type: 'failed', payload: null })
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="mx-auto w-full">
|
|
<h2 className="title-4xl-semi-bold text-text-secondary">{t('oneMoreStep', { ns: 'login' })}</h2>
|
|
<p className="mt-1 body-md-regular text-text-tertiary">{t('createSample', { ns: 'login' })}</p>
|
|
</div>
|
|
|
|
<div className="mx-auto mt-6 w-full">
|
|
<div className="relative">
|
|
<div className="mb-5">
|
|
<label className="my-2 flex items-center justify-between system-md-semibold text-text-secondary">
|
|
{t('invitationCode', { ns: 'login' })}
|
|
<Tooltip
|
|
popupContent={(
|
|
<div className="w-[256px] text-xs font-medium">
|
|
<div className="font-medium">{t('sendUsMail', { ns: 'login' })}</div>
|
|
<div className="cursor-pointer text-xs font-medium text-text-accent-secondary">
|
|
<a href="mailto:request-invitation@langgenius.ai">request-invitation@langgenius.ai</a>
|
|
</div>
|
|
</div>
|
|
)}
|
|
>
|
|
<span className="cursor-pointer text-text-accent-secondary">{t('dontHave', { ns: 'login' })}</span>
|
|
</Tooltip>
|
|
</label>
|
|
<div className="mt-1">
|
|
<Input
|
|
id="invitation_code"
|
|
value={state.invitation_code}
|
|
type="text"
|
|
placeholder={t('invitationCodePlaceholder', { ns: 'login' }) || ''}
|
|
onChange={(e) => {
|
|
dispatch({ type: 'invitation_code', value: e.target.value.trim() })
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="mb-5">
|
|
<label htmlFor="name" className="my-2 system-md-semibold text-text-secondary">
|
|
{t('interfaceLanguage', { ns: 'login' })}
|
|
</label>
|
|
<div className="mt-1">
|
|
<Select
|
|
value={selectedLanguage?.value ?? null}
|
|
onValueChange={(nextValue) => {
|
|
if (!nextValue)
|
|
return
|
|
dispatch({ type: 'interface_language', value: nextValue as typeof LanguagesSupported[number] })
|
|
}}
|
|
>
|
|
<SelectTrigger size="large">
|
|
{selectedLanguage?.name ?? t('placeholder.select', { ns: 'common' })}
|
|
</SelectTrigger>
|
|
<SelectContent popupClassName="w-(--anchor-width)">
|
|
{languageOptions.map(item => (
|
|
<SelectItem key={item.value} value={item.value}>
|
|
<SelectItemText>{item.name}</SelectItemText>
|
|
<SelectItemIndicator />
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div className="mb-4">
|
|
<label htmlFor="timezone" className="system-md-semibold text-text-tertiary">
|
|
{t('timezone', { ns: 'login' })}
|
|
</label>
|
|
<div className="mt-1">
|
|
<Select
|
|
value={selectedTimezone ? String(selectedTimezone.value) : null}
|
|
onValueChange={(nextValue) => {
|
|
if (!nextValue)
|
|
return
|
|
dispatch({ type: 'timezone', value: nextValue as typeof state.timezone })
|
|
}}
|
|
>
|
|
<SelectTrigger size="large">
|
|
{selectedTimezone?.name ?? t('placeholder.select', { ns: 'common' })}
|
|
</SelectTrigger>
|
|
<SelectContent popupClassName="w-(--anchor-width)">
|
|
{timezones.map(item => (
|
|
<SelectItem key={item.value} value={String(item.value)}>
|
|
<SelectItemText>{item.name}</SelectItemText>
|
|
<SelectItemIndicator />
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<Button
|
|
variant="primary"
|
|
className="w-full"
|
|
disabled={isPending}
|
|
onClick={handleSubmit}
|
|
>
|
|
{t('go', { ns: 'login' })}
|
|
</Button>
|
|
</div>
|
|
<div className="mt-2 block w-full system-xs-regular text-text-tertiary">
|
|
{t('license.tip', { ns: 'login' })}
|
|
|
|
<Link
|
|
className="system-xs-medium text-text-accent-secondary"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href={LICENSE_LINK}
|
|
>
|
|
{t('license.link', { ns: 'login' })}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default OneMoreStep
|