mirror of
https://github.com/langgenius/dify.git
synced 2026-08-28 19:38:30 +08:00
174 lines
6.4 KiB
TypeScript
174 lines
6.4 KiB
TypeScript
'use client'
|
|
|
|
import { Button } from '@langgenius/dify-ui/button'
|
|
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
|
import { toast } from '@langgenius/dify-ui/toast'
|
|
import { RiCloseLine } from '@remixicon/react'
|
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import AppIcon from '@/app/components/base/app-icon'
|
|
import { MARKETPLACE_API_PREFIX } from '@/config'
|
|
import {
|
|
fetchMarketplaceTemplateDSL,
|
|
useMarketplaceTemplateDetail,
|
|
} from '@/service/marketplace-templates'
|
|
|
|
type ImportFromMarketplaceTemplateModalProps = {
|
|
templateId: string
|
|
onClose: () => void
|
|
onConfirm: (dslContent: string) => void
|
|
}
|
|
|
|
const ImportFromMarketplaceTemplateModal = ({
|
|
templateId,
|
|
onClose,
|
|
onConfirm,
|
|
}: ImportFromMarketplaceTemplateModalProps) => {
|
|
const { t } = useTranslation()
|
|
const { data, isLoading, isError } = useMarketplaceTemplateDetail(templateId)
|
|
const template = data?.data
|
|
const [importing, setImporting] = useState(false)
|
|
const isImportingRef = useRef(false)
|
|
|
|
const CATEGORY_I18N_MAP: Record<string, string> = useMemo(
|
|
() => ({
|
|
marketing: t(($) => $['marketplace.template.category.marketing'], { ns: 'app' }),
|
|
sales: t(($) => $['marketplace.template.category.sales'], { ns: 'app' }),
|
|
support: t(($) => $['marketplace.template.category.support'], { ns: 'app' }),
|
|
operations: t(($) => $['marketplace.template.category.operations'], { ns: 'app' }),
|
|
it: t(($) => $['marketplace.template.category.it'], { ns: 'app' }),
|
|
knowledge: t(($) => $['marketplace.template.category.knowledge'], { ns: 'app' }),
|
|
design: t(($) => $['marketplace.template.category.design'], { ns: 'app' }),
|
|
}),
|
|
[t],
|
|
)
|
|
|
|
const translateCategory = useCallback(
|
|
(slug: string) => {
|
|
return CATEGORY_I18N_MAP[slug] ?? slug
|
|
},
|
|
[CATEGORY_I18N_MAP],
|
|
)
|
|
|
|
const handleConfirm = useCallback(async () => {
|
|
if (isImportingRef.current) return
|
|
isImportingRef.current = true
|
|
setImporting(true)
|
|
try {
|
|
const dsl = await fetchMarketplaceTemplateDSL(templateId)
|
|
onConfirm(dsl)
|
|
} catch {
|
|
toast.error(t(($) => $['marketplace.template.importFailed'], { ns: 'app' }))
|
|
} finally {
|
|
setImporting(false)
|
|
isImportingRef.current = false
|
|
}
|
|
}, [templateId, onConfirm, t])
|
|
|
|
return (
|
|
<Dialog
|
|
open
|
|
onOpenChange={(open) => {
|
|
if (!open) onClose()
|
|
}}
|
|
>
|
|
<DialogContent className="w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-0 shadow-xl">
|
|
<div className="flex items-center justify-between pt-6 pr-5 pb-3 pl-6 title-2xl-semi-bold text-text-primary">
|
|
{t(($) => $['marketplace.template.modalTitle'], { ns: 'app' })}
|
|
<div className="flex size-8 cursor-pointer items-center" onClick={onClose}>
|
|
<RiCloseLine className="size-5 text-text-tertiary" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="px-6 py-4">
|
|
{isLoading && (
|
|
<div className="flex items-center justify-center py-8">
|
|
<div className="system-md-regular text-text-tertiary">Loading...</div>
|
|
</div>
|
|
)}
|
|
|
|
{isError && (
|
|
<div className="flex items-center justify-center py-8">
|
|
<div className="system-md-regular text-text-destructive">
|
|
{t(($) => $['marketplace.template.fetchFailed'], { ns: 'app' })}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{template && (
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex items-center gap-3">
|
|
<AppIcon
|
|
size="large"
|
|
iconType={template.icon_file_key ? 'image' : 'emoji'}
|
|
icon={template.icon || 'page_facing_up'}
|
|
background={template.icon_file_key ? undefined : template.icon_background}
|
|
imageUrl={
|
|
template.icon_file_key
|
|
? `${MARKETPLACE_API_PREFIX}/templates/${template.id}/icon`
|
|
: undefined
|
|
}
|
|
/>
|
|
<div className="flex flex-col">
|
|
<div className="system-md-semibold text-text-primary">
|
|
{template.template_name}
|
|
</div>
|
|
<div className="flex items-center gap-1 system-xs-regular text-text-tertiary">
|
|
<span>
|
|
{t(($) => $['marketplace.template.publishedBy'], { ns: 'app' })}{' '}
|
|
{template.publisher_unique_handle}
|
|
</span>
|
|
<span>·</span>
|
|
<span>
|
|
{t(($) => $['marketplace.template.usageCount'], { ns: 'app' })}{' '}
|
|
{template.usage_count}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{template.overview && (
|
|
<div className="flex flex-col gap-1">
|
|
<div className="system-xs-medium-uppercase text-text-tertiary">
|
|
{t(($) => $['marketplace.template.overview'], { ns: 'app' })}
|
|
</div>
|
|
<div className="system-sm-regular text-text-secondary">{template.overview}</div>
|
|
</div>
|
|
)}
|
|
|
|
{template.categories.length > 0 && (
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
{template.categories.map((cat) => (
|
|
<span
|
|
key={cat}
|
|
className="inline-flex items-center rounded-full bg-components-label-gray px-2.5 py-1 system-sm-regular text-text-secondary"
|
|
>
|
|
{translateCategory(cat)}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex justify-end px-6 py-5">
|
|
<Button className="mr-2" onClick={onClose}>
|
|
{t(($) => $['newApp.Cancel'], { ns: 'app' })}
|
|
</Button>
|
|
<Button
|
|
variant="primary"
|
|
disabled={isLoading || isError || importing}
|
|
loading={importing}
|
|
onClick={handleConfirm}
|
|
>
|
|
{t(($) => $['marketplace.template.importConfirm'], { ns: 'app' })}
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
export default ImportFromMarketplaceTemplateModal
|