Browse Source

feat: option to hide workflow steps (#5436)

crazywoola 10 months ago
parent
commit
92ddb410cd

+ 3 - 1
api/controllers/console/app/site.py

@@ -28,6 +28,7 @@ def parse_app_site_args():
                         required=False,
                         required=False,
                         location='json')
                         location='json')
     parser.add_argument('prompt_public', type=bool, required=False, location='json')
     parser.add_argument('prompt_public', type=bool, required=False, location='json')
+    parser.add_argument('show_workflow_steps', type=bool, required=False, location='json')
     return parser.parse_args()
     return parser.parse_args()
 
 
 
 
@@ -59,7 +60,8 @@ class AppSite(Resource):
             'privacy_policy',
             'privacy_policy',
             'custom_disclaimer',
             'custom_disclaimer',
             'customize_token_strategy',
             'customize_token_strategy',
-            'prompt_public'
+            'prompt_public',
+            'show_workflow_steps'
         ]:
         ]:
             value = args.get(attr_name)
             value = args.get(attr_name)
             if value is not None:
             if value is not None:

+ 2 - 1
api/controllers/web/site.py

@@ -33,7 +33,8 @@ class AppSiteApi(WebApiResource):
         'privacy_policy': fields.String,
         'privacy_policy': fields.String,
         'custom_disclaimer': fields.String,
         'custom_disclaimer': fields.String,
         'default_language': fields.String,
         'default_language': fields.String,
-        'prompt_public': fields.Boolean
+        'prompt_public': fields.Boolean,
+        'show_workflow_steps': fields.Boolean,
     }
     }
 
 
     app_fields = {
     app_fields = {

+ 3 - 1
api/fields/app_fields.py

@@ -117,6 +117,7 @@ site_fields = {
     'customize_token_strategy': fields.String,
     'customize_token_strategy': fields.String,
     'prompt_public': fields.Boolean,
     'prompt_public': fields.Boolean,
     'app_base_url': fields.String,
     'app_base_url': fields.String,
+    'show_workflow_steps': fields.Boolean,
 }
 }
 
 
 app_detail_fields_with_site = {
 app_detail_fields_with_site = {
@@ -149,5 +150,6 @@ app_site_fields = {
     'privacy_policy': fields.String,
     'privacy_policy': fields.String,
     'custom_disclaimer': fields.String,
     'custom_disclaimer': fields.String,
     'customize_token_strategy': fields.String,
     'customize_token_strategy': fields.String,
-    'prompt_public': fields.Boolean
+    'prompt_public': fields.Boolean,
+    'show_workflow_steps': fields.Boolean,
 }
 }

+ 33 - 0
api/migrations/versions/4ff534e1eb11_add_workflow_to_site.py

@@ -0,0 +1,33 @@
+"""add workflow to site
+
+Revision ID: 4ff534e1eb11
+Revises: 7b45942e39bb
+Create Date: 2024-06-21 04:16:03.419634
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+import models as models
+
+# revision identifiers, used by Alembic.
+revision = '4ff534e1eb11'
+down_revision = '7b45942e39bb'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    with op.batch_alter_table('sites', schema=None) as batch_op:
+        batch_op.add_column(sa.Column('show_workflow_steps', sa.Boolean(), server_default=sa.text('true'), nullable=False))
+
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    with op.batch_alter_table('sites', schema=None) as batch_op:
+        batch_op.drop_column('show_workflow_steps')
+
+    # ### end Alembic commands ###

+ 1 - 0
api/models/model.py

@@ -1043,6 +1043,7 @@ class Site(db.Model):
     default_language = db.Column(db.String(255), nullable=False)
     default_language = db.Column(db.String(255), nullable=False)
     copyright = db.Column(db.String(255))
     copyright = db.Column(db.String(255))
     privacy_policy = db.Column(db.String(255))
     privacy_policy = db.Column(db.String(255))
+    show_workflow_steps = db.Column(db.Boolean, nullable=False, server_default=db.text('true'))
     custom_disclaimer = db.Column(db.String(255), nullable=True)
     custom_disclaimer = db.Column(db.String(255), nullable=True)
     customize_domain = db.Column(db.String(255))
     customize_domain = db.Column(db.String(255))
     customize_token_strategy = db.Column(db.String(255), nullable=False)
     customize_token_strategy = db.Column(db.String(255), nullable=False)

+ 13 - 3
web/app/components/app/overview/settings/index.tsx

@@ -34,6 +34,7 @@ export type ConfigParams = {
   custom_disclaimer: string
   custom_disclaimer: string
   icon: string
   icon: string
   icon_background: string
   icon_background: string
+  show_workflow_steps: boolean
 }
 }
 
 
 const prefixSettings = 'appOverview.overview.appInfo.settings'
 const prefixSettings = 'appOverview.overview.appInfo.settings'
@@ -47,8 +48,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
   const { notify } = useToastContext()
   const { notify } = useToastContext()
   const [isShowMore, setIsShowMore] = useState(false)
   const [isShowMore, setIsShowMore] = useState(false)
   const { icon, icon_background } = appInfo
   const { icon, icon_background } = appInfo
-  const { title, description, copyright, privacy_policy, custom_disclaimer, default_language } = appInfo.site
-  const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer })
+  const { title, description, copyright, privacy_policy, custom_disclaimer, default_language, show_workflow_steps } = appInfo.site
+  const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps })
   const [language, setLanguage] = useState(default_language)
   const [language, setLanguage] = useState(default_language)
   const [saveLoading, setSaveLoading] = useState(false)
   const [saveLoading, setSaveLoading] = useState(false)
   const { t } = useTranslation()
   const { t } = useTranslation()
@@ -57,7 +58,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
   const [emoji, setEmoji] = useState({ icon, icon_background })
   const [emoji, setEmoji] = useState({ icon, icon_background })
 
 
   useEffect(() => {
   useEffect(() => {
-    setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer })
+    setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps })
     setLanguage(default_language)
     setLanguage(default_language)
     setEmoji({ icon, icon_background })
     setEmoji({ icon, icon_background })
   }, [appInfo])
   }, [appInfo])
@@ -85,6 +86,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
       custom_disclaimer: inputInfo.customDisclaimer,
       custom_disclaimer: inputInfo.customDisclaimer,
       icon: emoji.icon,
       icon: emoji.icon,
       icon_background: emoji.icon_background,
       icon_background: emoji.icon_background,
+      show_workflow_steps: inputInfo.show_workflow_steps,
     }
     }
     await onSave?.(params)
     await onSave?.(params)
     setSaveLoading(false)
     setSaveLoading(false)
@@ -134,6 +136,14 @@ const SettingsModal: FC<ISettingsModalProps> = ({
           defaultValue={language}
           defaultValue={language}
           onSelect={item => setLanguage(item.value as Language)}
           onSelect={item => setLanguage(item.value as Language)}
         />
         />
+        {(appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat') && <>
+          <div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.workflow.title`)}</div>
+          <SimpleSelect
+            items={[{ name: t(`${prefixSettings}.workflow.show`), value: 'true' }, { name: t(`${prefixSettings}.workflow.hide`), value: 'false' }]}
+            defaultValue={inputInfo.show_workflow_steps ? 'true' : 'false'}
+            onSelect={item => setInputInfo({ ...inputInfo, show_workflow_steps: item.value === 'true' })}
+          />
+        </>}
         {!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}>
         {!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}>
           <div className='flex justify-between'>
           <div className='flex justify-between'>
             <div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div>
             <div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div>

+ 5 - 1
web/app/components/app/text-generate/item/index.tsx

@@ -28,6 +28,7 @@ import EditReplyModal from '@/app/components/app/annotation/edit-annotation-moda
 import { useStore as useAppStore } from '@/app/components/app/store'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process'
 import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process'
 import type { WorkflowProcess } from '@/app/components/base/chat/types'
 import type { WorkflowProcess } from '@/app/components/base/chat/types'
+import type { SiteInfo } from '@/models/share'
 
 
 const MAX_DEPTH = 3
 const MAX_DEPTH = 3
 
 
@@ -62,6 +63,7 @@ export type IGenerationItemProps = {
   contentClassName?: string
   contentClassName?: string
   footerClassName?: string
   footerClassName?: string
   hideProcessDetail?: boolean
   hideProcessDetail?: boolean
+  siteInfo: SiteInfo | null
 }
 }
 
 
 export const SimpleBtn = ({ className, isDisabled, onClick, children }: {
 export const SimpleBtn = ({ className, isDisabled, onClick, children }: {
@@ -113,6 +115,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
   innerClassName,
   innerClassName,
   contentClassName,
   contentClassName,
   hideProcessDetail,
   hideProcessDetail,
+  siteInfo,
 }) => {
 }) => {
   const { t } = useTranslation()
   const { t } = useTranslation()
   const params = useParams()
   const params = useParams()
@@ -152,6 +155,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
     installedAppId,
     installedAppId,
     controlClearMoreLikeThis,
     controlClearMoreLikeThis,
     isWorkflow,
     isWorkflow,
+    siteInfo,
   }
   }
 
 
   const handleMoreLikeThis = async () => {
   const handleMoreLikeThis = async () => {
@@ -297,7 +301,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
             }
             }
             <div className={`flex ${contentClassName}`}>
             <div className={`flex ${contentClassName}`}>
               <div className='grow w-0'>
               <div className='grow w-0'>
-                {workflowProcessData && (
+                {siteInfo && siteInfo.show_workflow_steps && workflowProcessData && (
                   <WorkflowProcessItem grayBg hideInfo data={workflowProcessData} expand={workflowProcessData.expand} hideProcessDetail={hideProcessDetail} />
                   <WorkflowProcessItem grayBg hideInfo data={workflowProcessData} expand={workflowProcessData.expand} hideProcessDetail={hideProcessDetail} />
                 )}
                 )}
                 {workflowProcessData && !isError && (
                 {workflowProcessData && !isError && (

+ 2 - 0
web/app/components/base/chat/chat-with-history/chat-wrapper.tsx

@@ -29,6 +29,7 @@ const ChatWrapper = () => {
     appMeta,
     appMeta,
     handleFeedback,
     handleFeedback,
     currentChatInstanceRef,
     currentChatInstanceRef,
+    appData,
   } = useChatWithHistoryContext()
   } = useChatWithHistoryContext()
   const appConfig = useMemo(() => {
   const appConfig = useMemo(() => {
     const config = appParams || {}
     const config = appParams || {}
@@ -128,6 +129,7 @@ const ChatWrapper = () => {
 
 
   return (
   return (
     <Chat
     <Chat
+      appData={appData}
       config={appConfig}
       config={appConfig}
       chatList={chatList}
       chatList={chatList}
       isResponding={isResponding}
       isResponding={isResponding}

+ 8 - 1
web/app/components/base/chat/chat-with-history/hooks.tsx

@@ -47,7 +47,14 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
       const { id, app } = installedAppInfo!
       const { id, app } = installedAppInfo!
       return {
       return {
         app_id: id,
         app_id: id,
-        site: { title: app.name, icon: app.icon, icon_background: app.icon_background, prompt_public: false, copyright: '' },
+        site: {
+          title: app.name,
+          icon: app.icon,
+          icon_background: app.icon_background,
+          prompt_public: false,
+          copyright: '',
+          show_workflow_steps: true,
+        },
         plan: 'basic',
         plan: 'basic',
       } as AppData
       } as AppData
     }
     }

+ 16 - 1
web/app/components/base/chat/chat/answer/index.tsx

@@ -20,6 +20,7 @@ import LoadingAnim from '@/app/components/app/chat/loading-anim'
 import Citation from '@/app/components/app/chat/citation'
 import Citation from '@/app/components/app/chat/citation'
 import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
 import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
 import type { Emoji } from '@/app/components/tools/types'
 import type { Emoji } from '@/app/components/tools/types'
+import type { AppData } from '@/models/share'
 
 
 type AnswerProps = {
 type AnswerProps = {
   item: ChatItem
   item: ChatItem
@@ -32,6 +33,7 @@ type AnswerProps = {
   showPromptLog?: boolean
   showPromptLog?: boolean
   chatAnswerContainerInner?: string
   chatAnswerContainerInner?: string
   hideProcessDetail?: boolean
   hideProcessDetail?: boolean
+  appData?: AppData
 }
 }
 const Answer: FC<AnswerProps> = ({
 const Answer: FC<AnswerProps> = ({
   item,
   item,
@@ -44,6 +46,7 @@ const Answer: FC<AnswerProps> = ({
   showPromptLog,
   showPromptLog,
   chatAnswerContainerInner,
   chatAnswerContainerInner,
   hideProcessDetail,
   hideProcessDetail,
+  appData,
 }) => {
 }) => {
   const { t } = useTranslation()
   const { t } = useTranslation()
   const {
   const {
@@ -129,8 +132,20 @@ const Answer: FC<AnswerProps> = ({
                 />
                 />
               )
               )
             }
             }
+            {/** Render the normal steps */}
             {
             {
-              workflowProcess && (
+              workflowProcess && !hideProcessDetail && (
+                <WorkflowProcess
+                  data={workflowProcess}
+                  item={item}
+                  hideInfo
+                  hideProcessDetail={hideProcessDetail}
+                />
+              )
+            }
+            {/** Hide workflow steps by it's settings in siteInfo */}
+            {
+              workflowProcess && hideProcessDetail && appData && appData.site.show_workflow_steps && (
                 <WorkflowProcess
                 <WorkflowProcess
                   data={workflowProcess}
                   data={workflowProcess}
                   item={item}
                   item={item}

+ 4 - 0
web/app/components/base/chat/chat/index.tsx

@@ -30,8 +30,10 @@ import { StopCircle } from '@/app/components/base/icons/src/vender/solid/mediaAn
 import AgentLogModal from '@/app/components/base/agent-log-modal'
 import AgentLogModal from '@/app/components/base/agent-log-modal'
 import PromptLogModal from '@/app/components/base/prompt-log-modal'
 import PromptLogModal from '@/app/components/base/prompt-log-modal'
 import { useStore as useAppStore } from '@/app/components/app/store'
 import { useStore as useAppStore } from '@/app/components/app/store'
+import type { AppData } from '@/models/share'
 
 
 export type ChatProps = {
 export type ChatProps = {
+  appData?: AppData
   chatList: ChatItem[]
   chatList: ChatItem[]
   config?: ChatConfig
   config?: ChatConfig
   isResponding?: boolean
   isResponding?: boolean
@@ -57,6 +59,7 @@ export type ChatProps = {
   hideProcessDetail?: boolean
   hideProcessDetail?: boolean
 }
 }
 const Chat: FC<ChatProps> = ({
 const Chat: FC<ChatProps> = ({
+  appData,
   config,
   config,
   onSend,
   onSend,
   chatList,
   chatList,
@@ -196,6 +199,7 @@ const Chat: FC<ChatProps> = ({
                   const isLast = item.id === chatList[chatList.length - 1]?.id
                   const isLast = item.id === chatList[chatList.length - 1]?.id
                   return (
                   return (
                     <Answer
                     <Answer
+                      appData={appData}
                       key={item.id}
                       key={item.id}
                       item={item}
                       item={item}
                       question={chatList[index - 1]?.content}
                       question={chatList[index - 1]?.content}

+ 2 - 0
web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx

@@ -18,6 +18,7 @@ import LogoAvatar from '@/app/components/base/logo/logo-embeded-chat-avatar'
 
 
 const ChatWrapper = () => {
 const ChatWrapper = () => {
   const {
   const {
+    appData,
     appParams,
     appParams,
     appPrevChatList,
     appPrevChatList,
     currentConversationId,
     currentConversationId,
@@ -114,6 +115,7 @@ const ChatWrapper = () => {
 
 
   return (
   return (
     <Chat
     <Chat
+      appData={appData}
       config={appConfig}
       config={appConfig}
       chatList={chatList}
       chatList={chatList}
       isResponding={isResponding}
       isResponding={isResponding}

+ 1 - 0
web/app/components/share/text-generation/index.tsx

@@ -442,6 +442,7 @@ const TextGeneration: FC<IMainProps> = ({
     visionConfig={visionConfig}
     visionConfig={visionConfig}
     completionFiles={completionFiles}
     completionFiles={completionFiles}
     isShowTextToSpeech={!!textToSpeechConfig?.enabled}
     isShowTextToSpeech={!!textToSpeechConfig?.enabled}
+    siteInfo={siteInfo}
   />)
   />)
 
 
   const renderBatchRes = () => {
   const renderBatchRes = () => {

+ 4 - 0
web/app/components/share/text-generation/result/index.tsx

@@ -18,6 +18,7 @@ import { TransferMethod, type VisionFile, type VisionSettings } from '@/types/ap
 import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types'
 import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types'
 import type { WorkflowProcess } from '@/app/components/base/chat/types'
 import type { WorkflowProcess } from '@/app/components/base/chat/types'
 import { sleep } from '@/utils'
 import { sleep } from '@/utils'
+import type { SiteInfo } from '@/models/share'
 
 
 export type IResultProps = {
 export type IResultProps = {
   isWorkflow: boolean
   isWorkflow: boolean
@@ -42,6 +43,7 @@ export type IResultProps = {
   moderationService?: (text: string) => ReturnType<ModerationService>
   moderationService?: (text: string) => ReturnType<ModerationService>
   visionConfig: VisionSettings
   visionConfig: VisionSettings
   completionFiles: VisionFile[]
   completionFiles: VisionFile[]
+  siteInfo: SiteInfo | null
 }
 }
 
 
 const Result: FC<IResultProps> = ({
 const Result: FC<IResultProps> = ({
@@ -65,6 +67,7 @@ const Result: FC<IResultProps> = ({
   onCompleted,
   onCompleted,
   visionConfig,
   visionConfig,
   completionFiles,
   completionFiles,
+  siteInfo,
 }) => {
 }) => {
   const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
   const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
   useEffect(() => {
   useEffect(() => {
@@ -375,6 +378,7 @@ const Result: FC<IResultProps> = ({
       controlClearMoreLikeThis={controlClearMoreLikeThis}
       controlClearMoreLikeThis={controlClearMoreLikeThis}
       isShowTextToSpeech={isShowTextToSpeech}
       isShowTextToSpeech={isShowTextToSpeech}
       hideProcessDetail
       hideProcessDetail
+      siteInfo={siteInfo}
     />
     />
   )
   )
 
 

+ 5 - 0
web/i18n/de-DE/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Dieser Text wird auf der Clientseite angezeigt und bietet grundlegende Anleitungen zur Verwendung der Anwendung',
         webDescTip: 'Dieser Text wird auf der Clientseite angezeigt und bietet grundlegende Anleitungen zur Verwendung der Anwendung',
         webDescPlaceholder: 'Geben Sie die Beschreibung der WebApp ein',
         webDescPlaceholder: 'Geben Sie die Beschreibung der WebApp ein',
         language: 'Sprache',
         language: 'Sprache',
+        workflow: {
+          title: 'Workflow-Schritte',
+          show: 'Anzeigen',
+          hide: 'Verbergen',
+        },
         more: {
         more: {
           entry: 'Mehr Einstellungen anzeigen',
           entry: 'Mehr Einstellungen anzeigen',
           copyright: 'Urheberrecht',
           copyright: 'Urheberrecht',

+ 5 - 0
web/i18n/en-US/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'This text will be displayed on the client side, providing basic guidance on how to use the application',
         webDescTip: 'This text will be displayed on the client side, providing basic guidance on how to use the application',
         webDescPlaceholder: 'Enter the description of the WebApp',
         webDescPlaceholder: 'Enter the description of the WebApp',
         language: 'Language',
         language: 'Language',
+        workflow: {
+          title: 'Workflow Steps',
+          show: 'Show',
+          hide: 'Hide',
+        },
         more: {
         more: {
           entry: 'Show more settings',
           entry: 'Show more settings',
           copyright: 'Copyright',
           copyright: 'Copyright',

+ 5 - 0
web/i18n/fr-FR/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Ce texte sera affiché côté client, fournissant des directives de base sur la façon d\'utiliser l\'application',
         webDescTip: 'Ce texte sera affiché côté client, fournissant des directives de base sur la façon d\'utiliser l\'application',
         webDescPlaceholder: 'Entrez la description de l\'application Web',
         webDescPlaceholder: 'Entrez la description de l\'application Web',
         language: 'Langue',
         language: 'Langue',
+        workflow: {
+          title: 'Étapes du workflow',
+          show: 'Afficher',
+          hide: 'Masquer',
+        },
         more: {
         more: {
           entry: 'Afficher plus de paramètres',
           entry: 'Afficher plus de paramètres',
           copyright: 'Droits d\'auteur',
           copyright: 'Droits d\'auteur',

+ 5 - 0
web/i18n/hi-IN/app-overview.ts

@@ -48,6 +48,11 @@ const translation = {
           'यह टेक्स्ट क्लाइंट साइड पर प्रदर्शित होगा, जो एप्लिकेशन का उपयोग करने के लिए बुनियादी मार्गदर्शन प्रदान करेगा',
           'यह टेक्स्ट क्लाइंट साइड पर प्रदर्शित होगा, जो एप्लिकेशन का उपयोग करने के लिए बुनियादी मार्गदर्शन प्रदान करेगा',
         webDescPlaceholder: 'वेबऐप का विवरण दर्ज करें',
         webDescPlaceholder: 'वेबऐप का विवरण दर्ज करें',
         language: 'भाषा',
         language: 'भाषा',
+        workflow: {
+          title: 'वर्कफ़्लो स्टेप्स',
+          show: 'दिखाएं',
+          hide: 'छुपाएं',
+        },
         more: {
         more: {
           entry: 'अधिक सेटिंग्स दिखाएं',
           entry: 'अधिक सेटिंग्स दिखाएं',
           copyright: 'कॉपीराइट',
           copyright: 'कॉपीराइट',

+ 5 - 0
web/i18n/ja-JP/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'このテキストはクライアント側に表示され、アプリケーションの使用方法の基本的なガイダンスを提供します。',
         webDescTip: 'このテキストはクライアント側に表示され、アプリケーションの使用方法の基本的なガイダンスを提供します。',
         webDescPlaceholder: 'WebAppの説明を入力してください',
         webDescPlaceholder: 'WebAppの説明を入力してください',
         language: '言語',
         language: '言語',
+        workflow: {
+          title: 'ワークフローステップ',
+          show: '表示',
+          hide: '非表示',
+        },
         more: {
         more: {
           entry: 'その他の設定を表示',
           entry: 'その他の設定を表示',
           copyright: '著作権',
           copyright: '著作権',

+ 5 - 0
web/i18n/ko-KR/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: '이 텍스트는 클라이언트 측에서 표시되며, 애플리케이션의 사용 방법에 대한 기본적인 안내를 제공합니다.',
         webDescTip: '이 텍스트는 클라이언트 측에서 표시되며, 애플리케이션의 사용 방법에 대한 기본적인 안내를 제공합니다.',
         webDescPlaceholder: '웹앱 설명을 입력하세요',
         webDescPlaceholder: '웹앱 설명을 입력하세요',
         language: '언어',
         language: '언어',
+        workflow: {
+          title: '워크플로 단계',
+          show: '표시',
+          hide: '숨기기',
+        },
         more: {
         more: {
           entry: '추가 설정 보기',
           entry: '추가 설정 보기',
           copyright: '저작권',
           copyright: '저작권',

+ 5 - 0
web/i18n/pl-PL/app-overview.ts

@@ -48,6 +48,11 @@ const translation = {
           'Ten tekst będzie wyświetlany po stronie klienta, zapewniając podstawowe wskazówki, jak korzystać z aplikacji',
           'Ten tekst będzie wyświetlany po stronie klienta, zapewniając podstawowe wskazówki, jak korzystać z aplikacji',
         webDescPlaceholder: 'Wpisz opis WebApp',
         webDescPlaceholder: 'Wpisz opis WebApp',
         language: 'Język',
         language: 'Język',
+        workflow: {
+          title: 'Kroki przepływu pracy',
+          show: 'Pokaż',
+          hide: 'Ukryj',
+        },
         more: {
         more: {
           entry: 'Pokaż więcej ustawień',
           entry: 'Pokaż więcej ustawień',
           copyright: 'Prawa autorskie',
           copyright: 'Prawa autorskie',

+ 5 - 0
web/i18n/pt-BR/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Este texto será exibido no lado do cliente, fornecendo orientações básicas sobre como usar o aplicativo',
         webDescTip: 'Este texto será exibido no lado do cliente, fornecendo orientações básicas sobre como usar o aplicativo',
         webDescPlaceholder: 'Insira a descrição do WebApp',
         webDescPlaceholder: 'Insira a descrição do WebApp',
         language: 'Idioma',
         language: 'Idioma',
+        workflow: {
+          title: 'Etapas do fluxo de trabalho',
+          show: 'Mostrar',
+          hide: 'Ocultar',
+        },
         more: {
         more: {
           entry: 'Mostrar mais configurações',
           entry: 'Mostrar mais configurações',
           copyright: 'Direitos autorais',
           copyright: 'Direitos autorais',

+ 5 - 0
web/i18n/ro-RO/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Acest text va fi afișat pe partea clientului, oferind îndrumare de bază privind modul de utilizare a aplicației',
         webDescTip: 'Acest text va fi afișat pe partea clientului, oferind îndrumare de bază privind modul de utilizare a aplicației',
         webDescPlaceholder: 'Introduceți descrierea aplicației web',
         webDescPlaceholder: 'Introduceți descrierea aplicației web',
         language: 'Limbă',
         language: 'Limbă',
+        workflow: {
+          title: 'Pași flux de lucru',
+          show: 'Afișați',
+          hide: 'Ascundeți',
+        },
         more: {
         more: {
           entry: 'Afișați mai multe setări',
           entry: 'Afișați mai multe setări',
           copyright: 'Drepturi de autor',
           copyright: 'Drepturi de autor',

+ 5 - 0
web/i18n/uk-UA/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Цей текст буде відображений на клієнтському боці, надаючи базові вказівки щодо використання додатка',
         webDescTip: 'Цей текст буде відображений на клієнтському боці, надаючи базові вказівки щодо використання додатка',
         webDescPlaceholder: 'Введіть опис веб-додатку',
         webDescPlaceholder: 'Введіть опис веб-додатку',
         language: 'Мова',
         language: 'Мова',
+        workflow: {
+          title: 'Кроки робочого процесу',
+          show: 'Показати',
+          hide: 'Приховати',
+        },
         more: {
         more: {
           entry: 'Показати додаткові налаштування',
           entry: 'Показати додаткові налаштування',
           copyright: 'Авторське право',
           copyright: 'Авторське право',

+ 5 - 0
web/i18n/vi-VN/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: 'Văn bản này sẽ được hiển thị ở phía máy khách, cung cấp hướng dẫn cơ bản về cách sử dụng ứng dụng',
         webDescTip: 'Văn bản này sẽ được hiển thị ở phía máy khách, cung cấp hướng dẫn cơ bản về cách sử dụng ứng dụng',
         webDescPlaceholder: 'Nhập mô tả của ứng dụng web',
         webDescPlaceholder: 'Nhập mô tả của ứng dụng web',
         language: 'Ngôn ngữ',
         language: 'Ngôn ngữ',
+        workflow: {
+          title: 'Các Bước Quy trình',
+          show: 'Hiển thị',
+          hide: 'Ẩn',
+        },
         more: {
         more: {
           entry: 'Hiển thị thêm cài đặt',
           entry: 'Hiển thị thêm cài đặt',
           copyright: 'Bản quyền',
           copyright: 'Bản quyền',

+ 5 - 0
web/i18n/zh-Hans/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: '以下文字将展示在客户端中,对应用进行说明和使用上的基本引导',
         webDescTip: '以下文字将展示在客户端中,对应用进行说明和使用上的基本引导',
         webDescPlaceholder: '请输入 WebApp 的描述',
         webDescPlaceholder: '请输入 WebApp 的描述',
         language: '语言',
         language: '语言',
+        workflow: {
+          title: '工作流详情',
+          show: '显示',
+          hide: '隐藏',
+        },
         more: {
         more: {
           entry: '展示更多设置',
           entry: '展示更多设置',
           copyright: '版权',
           copyright: '版权',

+ 5 - 0
web/i18n/zh-Hant/app-overview.ts

@@ -44,6 +44,11 @@ const translation = {
         webDescTip: '以下文字將展示在客戶端中,對應用進行說明和使用上的基本引導',
         webDescTip: '以下文字將展示在客戶端中,對應用進行說明和使用上的基本引導',
         webDescPlaceholder: '請輸入 WebApp 的描述',
         webDescPlaceholder: '請輸入 WebApp 的描述',
         language: '語言',
         language: '語言',
+        workflow: {
+          title: '工作流程步驟',
+          show: '展示',
+          hide: '隱藏',
+        },
         more: {
         more: {
           entry: '展示更多設定',
           entry: '展示更多設定',
           copyright: '版權',
           copyright: '版權',

+ 1 - 0
web/models/share.ts

@@ -19,6 +19,7 @@ export type SiteInfo = {
   copyright?: string
   copyright?: string
   privacy_policy?: string
   privacy_policy?: string
   custom_disclaimer?: string
   custom_disclaimer?: string
+  show_workflow_steps?: boolean
 }
 }
 
 
 export type AppMeta = {
 export type AppMeta = {

+ 2 - 0
web/types/app.ts

@@ -274,6 +274,8 @@ export type SiteConfig = {
 
 
   icon: string
   icon: string
   icon_background: string
   icon_background: string
+
+  show_workflow_steps: boolean
 }
 }
 
 
 /**
 /**