Bläddra i källkod

Fix: new upgrade page (#12417)

NFish 2 månader sedan
förälder
incheckning
9962118dbd
50 ändrade filer med 3953 tillägg och 602 borttagningar
  1. 2146 0
      web/app/components/base/grid-mask/Grid.svg
  2. 5 72
      web/app/components/base/grid-mask/index.tsx
  3. 5 0
      web/app/components/base/grid-mask/style.module.css
  4. 3 0
      web/app/components/base/icons/assets/public/billing/ar-cube-1.svg
  5. 5 0
      web/app/components/base/icons/assets/public/billing/asterisk.svg
  6. 23 0
      web/app/components/base/icons/assets/public/billing/aws-marketplace.svg
  7. 25 0
      web/app/components/base/icons/assets/public/billing/azure.svg
  8. 5 0
      web/app/components/base/icons/assets/public/billing/buildings.svg
  9. 5 0
      web/app/components/base/icons/assets/public/billing/diamond.svg
  10. 8 0
      web/app/components/base/icons/assets/public/billing/google-cloud.svg
  11. 3 0
      web/app/components/base/icons/assets/public/billing/group-2.svg
  12. 3 0
      web/app/components/base/icons/assets/public/billing/keyframe.svg
  13. 4 0
      web/app/components/base/icons/assets/public/billing/sparkles-soft.svg
  14. 29 0
      web/app/components/base/icons/src/public/billing/ArCube1.json
  15. 16 0
      web/app/components/base/icons/src/public/billing/ArCube1.tsx
  16. 38 0
      web/app/components/base/icons/src/public/billing/Asterisk.json
  17. 16 0
      web/app/components/base/icons/src/public/billing/Asterisk.tsx
  18. 179 0
      web/app/components/base/icons/src/public/billing/AwsMarketplace.json
  19. 16 0
      web/app/components/base/icons/src/public/billing/AwsMarketplace.tsx
  20. 193 0
      web/app/components/base/icons/src/public/billing/Azure.json
  21. 16 0
      web/app/components/base/icons/src/public/billing/Azure.tsx
  22. 39 0
      web/app/components/base/icons/src/public/billing/Buildings.json
  23. 16 0
      web/app/components/base/icons/src/public/billing/Buildings.tsx
  24. 39 0
      web/app/components/base/icons/src/public/billing/Diamond.json
  25. 16 0
      web/app/components/base/icons/src/public/billing/Diamond.tsx
  26. 66 0
      web/app/components/base/icons/src/public/billing/GoogleCloud.json
  27. 16 0
      web/app/components/base/icons/src/public/billing/GoogleCloud.tsx
  28. 29 0
      web/app/components/base/icons/src/public/billing/Group2.json
  29. 16 0
      web/app/components/base/icons/src/public/billing/Group2.tsx
  30. 28 0
      web/app/components/base/icons/src/public/billing/Keyframe.json
  31. 16 0
      web/app/components/base/icons/src/public/billing/Keyframe.tsx
  32. 36 0
      web/app/components/base/icons/src/public/billing/SparklesSoft.json
  33. 16 0
      web/app/components/base/icons/src/public/billing/SparklesSoft.tsx
  34. 10 0
      web/app/components/base/icons/src/public/billing/index.ts
  35. 2 2
      web/app/components/base/tab-slider/index.tsx
  36. 31 47
      web/app/components/billing/config.ts
  37. 5 5
      web/app/components/billing/plan/index.tsx
  38. 107 46
      web/app/components/billing/pricing/index.tsx
  39. 102 179
      web/app/components/billing/pricing/plan-item.tsx
  40. 24 25
      web/app/components/billing/pricing/select-plan-range.tsx
  41. 176 0
      web/app/components/billing/pricing/self-hosted-plan-item.tsx
  42. 28 9
      web/app/components/billing/type.ts
  43. 1 1
      web/app/components/billing/usage-info/apps-info.tsx
  44. 2 2
      web/app/components/billing/usage-info/vector-space-info.tsx
  45. 87 29
      web/i18n/en-US/billing.ts
  46. 90 33
      web/i18n/ja-JP/billing.ts
  47. 85 28
      web/i18n/zh-Hans/billing.ts
  48. 5 0
      web/tailwind-common-config.ts
  49. 61 62
      web/themes/manual-dark.css
  50. 61 62
      web/themes/manual-light.css

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2146 - 0
web/app/components/base/grid-mask/Grid.svg


+ 5 - 72
web/app/components/base/grid-mask/index.tsx

@@ -1,5 +1,6 @@
 import type { FC } from 'react'
-import { useCallback, useEffect, useRef } from 'react'
+import Style from './style.module.css'
+import classNames from '@/utils/classnames'
 
 type GridMaskProps = {
   children: React.ReactNode
@@ -13,78 +14,10 @@ const GridMask: FC<GridMaskProps> = ({
   canvasClassName,
   gradientClassName,
 }) => {
-  const canvasRef = useRef<HTMLCanvasElement | null>(null)
-  const ctxRef = useRef<CanvasRenderingContext2D | null>(null)
-  const initCanvas = () => {
-    const dpr = window.devicePixelRatio || 1
-
-    if (canvasRef.current) {
-      const { width: cssWidth, height: cssHeight } = canvasRef.current?.getBoundingClientRect()
-
-      canvasRef.current.width = dpr * cssWidth
-      canvasRef.current.height = dpr * cssHeight
-
-      const ctx = canvasRef.current.getContext('2d')
-      if (ctx) {
-        ctx.scale(dpr, dpr)
-        ctx.strokeStyle = '#D1E0FF'
-        ctxRef.current = ctx
-      }
-    }
-  }
-
-  const drawRecord = useCallback(() => {
-    const canvas = canvasRef.current!
-    const ctx = ctxRef.current!
-    const rowNumber = Number.parseInt(`${canvas.width / 24}`)
-    const colNumber = Number.parseInt(`${canvas.height / 24}`)
-
-    ctx.clearRect(0, 0, canvas.width, canvas.height)
-    ctx.beginPath()
-    for (let i = 0; i < rowNumber; i++) {
-      for (let j = 0; j < colNumber; j++) {
-        const x = i * 24
-        const y = j * 24
-        if (j === 0) {
-          ctx.moveTo(x, y + 2)
-          ctx.arc(x + 2, y + 2, 2, Math.PI, Math.PI * 1.5)
-          ctx.lineTo(x + 22, y)
-          ctx.arc(x + 22, y + 2, 2, Math.PI * 1.5, Math.PI * 2)
-          ctx.lineTo(x + 24, y + 22)
-          ctx.arc(x + 22, y + 22, 2, 0, Math.PI * 0.5)
-          ctx.lineTo(x + 2, y + 24)
-          ctx.arc(x + 2, y + 22, 2, Math.PI * 0.5, Math.PI)
-        }
-        else {
-          ctx.moveTo(x + 2, y)
-          ctx.arc(x + 2, y + 2, 2, Math.PI * 1.5, Math.PI, true)
-          ctx.lineTo(x, y + 22)
-          ctx.arc(x + 2, y + 22, 2, Math.PI, Math.PI * 0.5, true)
-          ctx.lineTo(x + 22, y + 24)
-          ctx.arc(x + 22, y + 22, 2, Math.PI * 0.5, 0, true)
-          ctx.lineTo(x + 24, y + 2)
-          ctx.arc(x + 22, y + 2, 2, 0, Math.PI * 1.5, true)
-        }
-      }
-    }
-    ctx.stroke()
-    ctx.closePath()
-  }, [])
-
-  const handleStartDraw = () => {
-    if (canvasRef.current && ctxRef.current)
-      drawRecord()
-  }
-
-  useEffect(() => {
-    initCanvas()
-    handleStartDraw()
-  }, [])
-
   return (
-    <div className={`relative bg-components-panel-bg ${wrapperClassName}`}>
-      <canvas ref={canvasRef} className={`absolute inset-0 w-full h-full ${canvasClassName}`} />
-      <div className={`absolute w-full h-full z-[1] bg-gradient-to-b from-background-body to-background-gradient-mask-transparent rounded-lg ${gradientClassName}`} />
+    <div className={classNames('relative bg-saas-background', wrapperClassName)}>
+      <div className={classNames('absolute inset-0 w-full h-full z-0 opacity-70', canvasClassName, Style.gridBg)} />
+      <div className={classNames('absolute w-full h-full z-[1] bg-grid-mask-background rounded-lg', gradientClassName)} />
       <div className='relative z-[2]'>{children}</div>
     </div>
   )

+ 5 - 0
web/app/components/base/grid-mask/style.module.css

@@ -0,0 +1,5 @@
+.gridBg{
+  background-image: url(./Grid.svg);
+  background-repeat: repeat;
+  background-position: 0 0;
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 0
web/app/components/base/icons/assets/public/billing/ar-cube-1.svg


+ 5 - 0
web/app/components/base/icons/assets/public/billing/asterisk.svg

@@ -0,0 +1,5 @@
+<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="asterisk">
+<path id="Vector" d="M14.0033 3.20837V24.7917M4.65747 8.60421L23.3492 19.3959M4.6586 19.3959L23.3503 8.60421" stroke="#101828" stroke-width="1.5" stroke-linecap="round"/>
+</g>
+</svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 23 - 0
web/app/components/base/icons/assets/public/billing/aws-marketplace.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 25 - 0
web/app/components/base/icons/assets/public/billing/azure.svg


+ 5 - 0
web/app/components/base/icons/assets/public/billing/buildings.svg

@@ -0,0 +1,5 @@
+<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="buildings">
+<path id="Vector" d="M5.04134 22.4583H17.2913M5.04134 22.4583V6.70833C5.04134 5.41967 6.08601 4.375 7.37467 4.375H14.958C16.2467 4.375 17.2913 5.41967 17.2913 6.70833V9.33333M5.04134 22.4583H2.70801M17.2913 22.4583V9.33333M17.2913 22.4583H24.2913M17.2913 9.33333H21.958C23.2467 9.33333 24.2913 10.378 24.2913 11.6667V22.4583M24.2913 22.4583H26.6247M12.6247 10.2083H9.70801M9.70801 14.875H12.6247" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

+ 5 - 0
web/app/components/base/icons/assets/public/billing/diamond.svg

@@ -0,0 +1,5 @@
+<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="diamond">
+<path id="Vector" d="M10.2499 9.04167L7.62486 11.6667L10.2499 14.2917M15.9831 22.8501L25.4978 13.3353C26.4164 12.4168 26.408 10.925 25.4791 10.017L20.3883 5.03988C19.9523 4.61365 19.3668 4.375 18.7571 4.375H9.90929C9.29958 4.375 8.71408 4.61365 8.27811 5.03988L3.18727 10.017C2.25844 10.925 2.25002 12.4168 3.16852 13.3353L12.6833 22.8501C13.5945 23.7613 15.0719 23.7613 15.9831 22.8501Z" stroke="#DC6803" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 8 - 0
web/app/components/base/icons/assets/public/billing/google-cloud.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 3 - 0
web/app/components/base/icons/assets/public/billing/group-2.svg


+ 3 - 0
web/app/components/base/icons/assets/public/billing/keyframe.svg

@@ -0,0 +1,3 @@
+<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15.9831 3.98321C15.072 3.072 13.5945 3.072 12.6833 3.98321L4.31648 12.35C3.40526 13.2612 3.40524 14.7386 4.31647 15.6499L12.6833 24.0167C13.5945 24.9279 15.072 24.9279 15.9831 24.0167L24.35 15.6499C25.2612 14.7386 25.2612 13.2612 24.35 12.35L15.9831 3.98321Z" stroke="#155AEF" stroke-width="1.5" stroke-linejoin="round"/>
+</svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4 - 0
web/app/components/base/icons/assets/public/billing/sparkles-soft.svg


+ 29 - 0
web/app/components/base/icons/src/public/billing/ArCube1.json

@@ -0,0 +1,29 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "28",
+			"height": "28",
+			"viewBox": "0 0 28 28",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M14.0002 14V23.9166M14.0002 14L5.25 9.07806M14.0002 14L22.4731 9.2338M23.625 9.95052V18.0493C23.625 18.8924 23.1703 19.6697 22.4356 20.0831L15.1439 24.1846C14.4336 24.5842 13.5663 24.5842 12.8561 24.1846L5.56439 20.0831C4.82967 19.6697 4.375 18.8924 4.375 18.0493V9.95052C4.375 9.10756 4.82967 8.33012 5.56439 7.91684L12.8561 3.81529C13.5663 3.41574 14.4336 3.41574 15.1439 3.81529L22.4356 7.91684C23.1703 8.33012 23.625 9.10756 23.625 9.95052Z",
+					"stroke": "#101828",
+					"stroke-width": "1.5",
+					"stroke-linecap": "round",
+					"stroke-linejoin": "round"
+				},
+				"children": []
+			}
+		]
+	},
+	"name": "ArCube1"
+}

+ 16 - 0
web/app/components/base/icons/src/public/billing/ArCube1.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './ArCube1.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'ArCube1'
+
+export default Icon

+ 38 - 0
web/app/components/base/icons/src/public/billing/Asterisk.json

@@ -0,0 +1,38 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "28",
+			"height": "28",
+			"viewBox": "0 0 28 28",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"id": "asterisk"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"id": "Vector",
+							"d": "M14.0033 3.20837V24.7917M4.65747 8.60421L23.3492 19.3959M4.6586 19.3959L23.3503 8.60421",
+							"stroke": "#101828",
+							"stroke-width": "1.5",
+							"stroke-linecap": "round"
+						},
+						"children": []
+					}
+				]
+			}
+		]
+	},
+	"name": "Asterisk"
+}

+ 16 - 0
web/app/components/base/icons/src/public/billing/Asterisk.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Asterisk.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Asterisk'
+
+export default Icon

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 179 - 0
web/app/components/base/icons/src/public/billing/AwsMarketplace.json


+ 16 - 0
web/app/components/base/icons/src/public/billing/AwsMarketplace.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './AwsMarketplace.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'AwsMarketplace'
+
+export default Icon

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 193 - 0
web/app/components/base/icons/src/public/billing/Azure.json


+ 16 - 0
web/app/components/base/icons/src/public/billing/Azure.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Azure.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Azure'
+
+export default Icon

+ 39 - 0
web/app/components/base/icons/src/public/billing/Buildings.json

@@ -0,0 +1,39 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "29",
+			"height": "28",
+			"viewBox": "0 0 29 28",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"id": "buildings"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"id": "Vector",
+							"d": "M5.04134 22.4583H17.2913M5.04134 22.4583V6.70833C5.04134 5.41967 6.08601 4.375 7.37467 4.375H14.958C16.2467 4.375 17.2913 5.41967 17.2913 6.70833V9.33333M5.04134 22.4583H2.70801M17.2913 22.4583V9.33333M17.2913 22.4583H24.2913M17.2913 9.33333H21.958C23.2467 9.33333 24.2913 10.378 24.2913 11.6667V22.4583M24.2913 22.4583H26.6247M12.6247 10.2083H9.70801M9.70801 14.875H12.6247",
+							"stroke": "white",
+							"stroke-width": "1.5",
+							"stroke-linecap": "round",
+							"stroke-linejoin": "round"
+						},
+						"children": []
+					}
+				]
+			}
+		]
+	},
+	"name": "Buildings"
+}

+ 16 - 0
web/app/components/base/icons/src/public/billing/Buildings.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Buildings.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Buildings'
+
+export default Icon

+ 39 - 0
web/app/components/base/icons/src/public/billing/Diamond.json

@@ -0,0 +1,39 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "29",
+			"height": "28",
+			"viewBox": "0 0 29 28",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "g",
+				"attributes": {
+					"id": "diamond"
+				},
+				"children": [
+					{
+						"type": "element",
+						"name": "path",
+						"attributes": {
+							"id": "Vector",
+							"d": "M10.2499 9.04167L7.62486 11.6667L10.2499 14.2917M15.9831 22.8501L25.4978 13.3353C26.4164 12.4168 26.408 10.925 25.4791 10.017L20.3883 5.03988C19.9523 4.61365 19.3668 4.375 18.7571 4.375H9.90929C9.29958 4.375 8.71408 4.61365 8.27811 5.03988L3.18727 10.017C2.25844 10.925 2.25002 12.4168 3.16852 13.3353L12.6833 22.8501C13.5945 23.7613 15.0719 23.7613 15.9831 22.8501Z",
+							"stroke": "#DC6803",
+							"stroke-width": "1.5",
+							"stroke-linecap": "round",
+							"stroke-linejoin": "round"
+						},
+						"children": []
+					}
+				]
+			}
+		]
+	},
+	"name": "Diamond"
+}

+ 16 - 0
web/app/components/base/icons/src/public/billing/Diamond.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Diamond.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Diamond'
+
+export default Icon

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 66 - 0
web/app/components/base/icons/src/public/billing/GoogleCloud.json


+ 16 - 0
web/app/components/base/icons/src/public/billing/GoogleCloud.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './GoogleCloud.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'GoogleCloud'
+
+export default Icon

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 29 - 0
web/app/components/base/icons/src/public/billing/Group2.json


+ 16 - 0
web/app/components/base/icons/src/public/billing/Group2.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Group2.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Group2'
+
+export default Icon

+ 28 - 0
web/app/components/base/icons/src/public/billing/Keyframe.json

@@ -0,0 +1,28 @@
+{
+	"icon": {
+		"type": "element",
+		"isRootNode": true,
+		"name": "svg",
+		"attributes": {
+			"width": "29",
+			"height": "28",
+			"viewBox": "0 0 29 28",
+			"fill": "none",
+			"xmlns": "http://www.w3.org/2000/svg"
+		},
+		"children": [
+			{
+				"type": "element",
+				"name": "path",
+				"attributes": {
+					"d": "M15.9831 3.98321C15.072 3.072 13.5945 3.072 12.6833 3.98321L4.31648 12.35C3.40526 13.2612 3.40524 14.7386 4.31647 15.6499L12.6833 24.0167C13.5945 24.9279 15.072 24.9279 15.9831 24.0167L24.35 15.6499C25.2612 14.7386 25.2612 13.2612 24.35 12.35L15.9831 3.98321Z",
+					"stroke": "#155AEF",
+					"stroke-width": "1.5",
+					"stroke-linejoin": "round"
+				},
+				"children": []
+			}
+		]
+	},
+	"name": "Keyframe"
+}

+ 16 - 0
web/app/components/base/icons/src/public/billing/Keyframe.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './Keyframe.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'Keyframe'
+
+export default Icon

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 36 - 0
web/app/components/base/icons/src/public/billing/SparklesSoft.json


+ 16 - 0
web/app/components/base/icons/src/public/billing/SparklesSoft.tsx

@@ -0,0 +1,16 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import data from './SparklesSoft.json'
+import IconBase from '@/app/components/base/icons/IconBase'
+import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
+
+const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
+  props,
+  ref,
+) => <IconBase {...props} ref={ref} data={data as IconData} />)
+
+Icon.displayName = 'SparklesSoft'
+
+export default Icon

+ 10 - 0
web/app/components/base/icons/src/public/billing/index.ts

@@ -1 +1,11 @@
+export { default as ArCube1 } from './ArCube1'
+export { default as Asterisk } from './Asterisk'
+export { default as AwsMarketplace } from './AwsMarketplace'
+export { default as Azure } from './Azure'
+export { default as Buildings } from './Buildings'
+export { default as Diamond } from './Diamond'
+export { default as GoogleCloud } from './GoogleCloud'
+export { default as Group2 } from './Group2'
+export { default as Keyframe } from './Keyframe'
+export { default as SparklesSoft } from './SparklesSoft'
 export { default as Sparkles } from './Sparkles'

+ 2 - 2
web/app/components/base/tab-slider/index.tsx

@@ -1,11 +1,11 @@
-import type { FC } from 'react'
+import type { FC, ReactNode } from 'react'
 import { useEffect, useState } from 'react'
 import cn from '@/utils/classnames'
 import Badge, { BadgeState } from '@/app/components/base/badge/index'
 import { useInstalledPluginList } from '@/service/use-plugins'
 type Option = {
   value: string
-  text: string
+  text: ReactNode
 }
 
 type TabSliderProps = {

+ 31 - 47
web/app/components/billing/config.ts

@@ -1,98 +1,82 @@
 import { Plan, type PlanInfo, Priority } from '@/app/components/billing/type'
 
-const supportModelProviders = 'OpenAI/Anthropic/Azure OpenAI/  Llama2/Hugging Face/Replicate'
+const supportModelProviders = 'OpenAI/Anthropic/Llama2/Azure OpenAI/Hugging Face/Replicate'
 
 export const NUM_INFINITE = 99999999
 export const contractSales = 'contractSales'
 export const unAvailable = 'unAvailable'
 
-export const contactSalesUrl = 'mailto:business@dify.ai'
+export const contactSalesUrl = 'https://vikgc6bnu1s.typeform.com/to/mowuXTQH'
+export const getStartedWithCommunityUrl = 'https://github.com/langgenius/dify'
+export const getWithPremiumUrl = 'https://aws.amazon.com/marketplace/pp/prodview-t22mebxzwjhu6'
 
 export const ALL_PLANS: Record<Plan, PlanInfo> = {
   sandbox: {
     level: 1,
     price: 0,
     modelProviders: supportModelProviders,
+    teamWorkspace: 1,
     teamMembers: 1,
-    buildApps: 10,
-    vectorSpace: 5,
-    documentsUploadQuota: 50,
+    buildApps: 5,
+    documents: 50,
+    vectorSpace: '50MB',
+    documentsUploadQuota: 0,
+    documentsRequestQuota: 10,
     documentProcessingPriority: Priority.standard,
-    logHistory: 30,
-    customTools: unAvailable,
-    messageRequest: {
-      en: '200 messages',
-      zh: '200 条信息',
-    },
+    messageRequest: 200,
     annotatedResponse: 10,
+    logHistory: 30,
   },
   professional: {
     level: 2,
     price: 59,
     modelProviders: supportModelProviders,
+    teamWorkspace: 1,
     teamMembers: 3,
     buildApps: 50,
-    vectorSpace: 200,
-    documentsUploadQuota: 500,
+    documents: 500,
+    vectorSpace: '5GB',
+    documentsUploadQuota: 0,
+    documentsRequestQuota: 100,
     documentProcessingPriority: Priority.priority,
-    logHistory: NUM_INFINITE,
-    customTools: 10,
-    messageRequest: {
-      en: '5,000  messages/month',
-      zh: '5,000 条信息/月',
-    },
+    messageRequest: 5000,
     annotatedResponse: 2000,
+    logHistory: NUM_INFINITE,
   },
   team: {
     level: 3,
     price: 159,
     modelProviders: supportModelProviders,
-    teamMembers: NUM_INFINITE,
-    buildApps: NUM_INFINITE,
-    vectorSpace: 1000,
-    documentsUploadQuota: 1000,
+    teamWorkspace: 1,
+    teamMembers: 50,
+    buildApps: 200,
+    documents: 1000,
+    vectorSpace: '20GB',
+    documentsUploadQuota: 0,
+    documentsRequestQuota: 1000,
     documentProcessingPriority: Priority.topPriority,
-    logHistory: NUM_INFINITE,
-    customTools: NUM_INFINITE,
-    messageRequest: {
-      en: '10,000  messages/month',
-      zh: '10,000 条信息/月',
-    },
+    messageRequest: 10000,
     annotatedResponse: 5000,
-  },
-  enterprise: {
-    level: 4,
-    price: 0,
-    modelProviders: supportModelProviders,
-    teamMembers: NUM_INFINITE,
-    buildApps: NUM_INFINITE,
-    vectorSpace: NUM_INFINITE,
-    documentsUploadQuota: NUM_INFINITE,
-    documentProcessingPriority: Priority.topPriority,
     logHistory: NUM_INFINITE,
-    customTools: NUM_INFINITE,
-    messageRequest: {
-      en: contractSales,
-      zh: contractSales,
-    },
-    annotatedResponse: NUM_INFINITE,
   },
 }
 
 export const defaultPlan = {
   type: Plan.sandbox,
   usage: {
+    documents: 50,
     vectorSpace: 1,
     buildApps: 1,
     teamMembers: 1,
     annotatedResponse: 1,
-    documentsUploadQuota: 1,
+    documentsUploadQuota: 0,
   },
   total: {
+    documents: 50,
     vectorSpace: 10,
     buildApps: 10,
     teamMembers: 1,
     annotatedResponse: 10,
-    documentsUploadQuota: 50,
+    documentsUploadQuota: 0,
   },
 }

+ 5 - 5
web/app/components/billing/plan/index.tsx

@@ -2,7 +2,7 @@
 import type { FC } from 'react'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import { Plan } from '../type'
+import { Plan, SelfHostedPlan } from '../type'
 import VectorSpaceInfo from '../usage-info/vector-space-info'
 import AppsInfo from '../usage-info/apps-info'
 import UpgradeBtn from '../upgrade-btn'
@@ -26,7 +26,7 @@ const typeStyle = {
     textClassNames: 'text-[#3538CD]',
     bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #E0EAFF',
   },
-  [Plan.enterprise]: {
+  [SelfHostedPlan.enterprise]: {
     textClassNames: 'text-[#DC6803]',
     bg: 'linear-gradient(113deg, rgba(255, 255, 255, 0.51) 3.51%, rgba(255, 255, 255, 0.00) 111.71%), #FFEED3',
   },
@@ -89,7 +89,7 @@ const PlanComp: FC<Props> = ({
         <UsageInfo
           className='py-3'
           Icon={User01}
-          name={t('billing.plansCommon.teamMembers')}
+          name={t('billing.usagePage.teamMembers')}
           usage={usage.teamMembers}
           total={total.teamMembers}
         />
@@ -98,14 +98,14 @@ const PlanComp: FC<Props> = ({
         <UsageInfo
           className='py-3'
           Icon={MessageFastPlus}
-          name={t('billing.plansCommon.annotationQuota')}
+          name={t('billing.usagePage.annotationQuota')}
           usage={usage.annotatedResponse}
           total={total.annotatedResponse}
         />
         <UsageInfo
           className='py-3'
           Icon={FileUpload}
-          name={t('billing.plansCommon.documentsUploadQuota')}
+          name={t('billing.usagePage.documentsUploadQuota')}
           usage={usage.documentsUploadQuota}
           total={total.documentsUploadQuota}
         />

+ 107 - 46
web/app/components/billing/pricing/index.tsx

@@ -3,13 +3,18 @@ import type { FC } from 'react'
 import React from 'react'
 import { createPortal } from 'react-dom'
 import { useTranslation } from 'react-i18next'
-import { RiCloseLine } from '@remixicon/react'
-import { Plan } from '../type'
+import { RiArrowRightUpLine, RiCloseLine, RiCloudFill, RiTerminalBoxFill } from '@remixicon/react'
+import Link from 'next/link'
+import { useKeyPress } from 'ahooks'
+import { Plan, SelfHostedPlan } from '../type'
+import TabSlider from '../../base/tab-slider'
 import SelectPlanRange, { PlanRange } from './select-plan-range'
 import PlanItem from './plan-item'
+import SelfHostedPlanItem from './self-hosted-plan-item'
 import { useProviderContext } from '@/context/provider-context'
 import GridMask from '@/app/components/base/grid-mask'
 import { useAppContext } from '@/context/app-context'
+import classNames from '@/utils/classnames'
 
 type Props = {
   onCancel: () => void
@@ -24,56 +29,112 @@ const Pricing: FC<Props> = ({
   const canPay = isCurrentWorkspaceManager
   const [planRange, setPlanRange] = React.useState<PlanRange>(PlanRange.monthly)
 
+  const [currentPlan, setCurrentPlan] = React.useState<string>('cloud')
+
+  useKeyPress(['esc'], onCancel)
+
   return createPortal(
     <div
-      className='fixed inset-0 flex bg-white z-[1000] overflow-auto'
+      className='fixed inset-0 top-0 right-0 bottom-0 left-0 p-4 bg-background-overlay-backdrop backdrop-blur-[6px] z-[1000]'
       onClick={e => e.stopPropagation()}
     >
-      <GridMask wrapperClassName='grow'>
-        <div className='grow width-[0] mt-6 p-6 flex flex-col items-center'>
-          <div className='mb-3 leading-[38px] text-[30px] font-semibold text-gray-900'>
-            {t('billing.plansCommon.title')}
-          </div>
-          <SelectPlanRange
-            value={planRange}
-            onChange={setPlanRange}
-          />
-          <div className='mt-8 pb-6 w-full justify-center flex-nowrap flex space-x-3'>
-            <PlanItem
-              currentPlan={plan.type}
-              plan={Plan.sandbox}
-              planRange={planRange}
-              canPay={canPay}
-            />
-            <PlanItem
-              currentPlan={plan.type}
-              plan={Plan.professional}
-              planRange={planRange}
-              canPay={canPay}
-            />
-            <PlanItem
-              currentPlan={plan.type}
-              plan={Plan.team}
-              planRange={planRange}
-              canPay={canPay}
-            />
-            <PlanItem
-              currentPlan={plan.type}
-              plan={Plan.enterprise}
-              planRange={planRange}
-              canPay={canPay}
-            />
-          </div>
+      <div className='w-full h-full relative overflow-auto rounded-2xl border border-effects-highlight bg-saas-background'>
+        <div
+          className='fixed top-7 right-7 flex items-center justify-center w-9 h-9 bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover rounded-[10px] cursor-pointer z-[1001]'
+          onClick={onCancel}
+        >
+          <RiCloseLine className='size-5 text-components-button-tertiary-text' />
         </div>
-      </GridMask>
+        <GridMask wrapperClassName='w-full min-h-full' canvasClassName='min-h-full'>
+          <div className='pt-12 px-8 pb-7 flex flex-col items-center'>
+            <div className='mb-2 title-5xl-bold text-text-primary'>
+              {t('billing.plansCommon.title')}
+            </div>
+            <div className='system-sm-regular text-text-secondary'>
+              <span>{t('billing.plansCommon.freeTrialTipPrefix')}</span>
+              <span className='text-gradient font-semibold'>{t('billing.plansCommon.freeTrialTip')}</span>
+              <span>{t('billing.plansCommon.freeTrialTipSuffix')}</span>
+            </div>
+          </div>
+          <div className='w-[1152px] mx-auto'>
+            <div className='py-2 flex items-center justify-between h-[64px]'>
+              <TabSlider
+                value={currentPlan}
+                itemWidth={170}
+                className='inline-flex'
+                options={[
+                  {
+                    value: 'cloud',
+                    text: <div className={
+                      classNames('inline-flex items-center system-md-semibold-uppercase text-text-secondary',
+                        currentPlan === 'cloud' && 'text-text-accent-light-mode-only')} >
+                      <RiCloudFill className='size-4 mr-2' />{t('billing.plansCommon.cloud')}</div>,
+                  },
+                  {
+                    value: 'self',
+                    text: <div className={
+                      classNames('inline-flex items-center system-md-semibold-uppercase text-text-secondary',
+                        currentPlan === 'self' && 'text-text-accent-light-mode-only')}>
+                      <RiTerminalBoxFill className='size-4 mr-2' />{t('billing.plansCommon.self')}</div>,
+                  }]}
+                onChange={v => setCurrentPlan(v)} />
 
-      <div
-        className='fixed top-6 right-6 flex items-center justify-center w-10 h-10 bg-black/[0.05] rounded-full backdrop-blur-[2px] cursor-pointer z-[1001]'
-        onClick={onCancel}
-      >
-        <RiCloseLine className='w-4 h-4 text-gray-900' />
-      </div>
-    </div>,
+              {currentPlan === 'cloud' && <SelectPlanRange
+                value={planRange}
+                onChange={setPlanRange}
+              />}
+            </div>
+            <div className='pt-3 pb-8'>
+              <div className='flex justify-center flex-nowrap gap-x-4'>
+                {currentPlan === 'cloud' && <>
+                  <PlanItem
+                    currentPlan={plan.type}
+                    plan={Plan.sandbox}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                  <PlanItem
+                    currentPlan={plan.type}
+                    plan={Plan.professional}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                  <PlanItem
+                    currentPlan={plan.type}
+                    plan={Plan.team}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                </>}
+                {currentPlan === 'self' && <>
+                  <SelfHostedPlanItem
+                    plan={SelfHostedPlan.community}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                  <SelfHostedPlanItem
+                    plan={SelfHostedPlan.premium}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                  <SelfHostedPlanItem
+                    plan={SelfHostedPlan.enterprise}
+                    planRange={planRange}
+                    canPay={canPay}
+                  />
+                </>}
+              </div>
+            </div>
+          </div>
+          <div className='py-4 flex items-center justify-center'>
+            <div className='px-3 py-2 flex items-center justify-center gap-x-0.5 text-components-button-secondary-accent-text rounded-lg hover:bg-state-accent-hover hover:cursor-pointer'>
+              <Link href='https://dify.ai/pricing#plans-and-features' className='system-sm-medium'>{t('billing.plansCommon.comparePlanAndFeatures')}</Link>
+              <RiArrowRightUpLine className='size-4' />
+            </div>
+          </div>
+        </GridMask>
+      </div >
+    </div >,
     document.body,
   )
 }

+ 102 - 179
web/app/components/billing/pricing/plan-item.tsx

@@ -1,18 +1,18 @@
 'use client'
-import type { FC } from 'react'
+import type { FC, ReactNode } from 'react'
 import React from 'react'
 import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
+import { RiApps2Line, RiBook2Line, RiBrain2Line, RiChatAiLine, RiFileEditLine, RiFolder6Line, RiGroupLine, RiHardDrive3Line, RiHistoryLine, RiProgress3Line, RiQuestionLine, RiSeoLine } from '@remixicon/react'
 import { Plan } from '../type'
-import { ALL_PLANS, NUM_INFINITE, contactSalesUrl, contractSales, unAvailable } from '../config'
+import { ALL_PLANS, NUM_INFINITE } from '../config'
 import Toast from '../../base/toast'
 import Tooltip from '../../base/tooltip'
+import Divider from '../../base/divider'
+import { ArCube1, Group2, Keyframe, SparklesSoft } from '../../base/icons/src/public/billing'
 import { PlanRange } from './select-plan-range'
 import cn from '@/utils/classnames'
 import { useAppContext } from '@/context/app-context'
 import { fetchSubscriptionUrls } from '@/service/billing'
-import { LanguagesSupported } from '@/i18n/language'
-import I18n from '@/context/i18n'
 
 type Props = {
   currentPlan: Plan
@@ -21,162 +21,76 @@ type Props = {
   canPay: boolean
 }
 
-const KeyValue = ({ label, value, tooltip }: { label: string; value: string | number | JSX.Element; tooltip?: string }) => {
+const KeyValue = ({ icon, label, tooltip }: { icon: ReactNode; label: string; tooltip?: ReactNode }) => {
   return (
-    <div className='mt-3.5 leading-[125%] text-[13px] font-medium'>
-      <div className='flex items-center text-gray-500 space-x-1'>
-        <div>{label}</div>
-        {tooltip && (
-          <Tooltip
-            popupContent={
-              <div className='w-[200px]'>{tooltip}</div>
-            }
-          />
-        )}
+    <div className='flex text-text-tertiary'>
+      <div className='size-4 flex items-center justify-center'>
+        {icon}
       </div>
-      <div className='mt-0.5 text-gray-900'>{value}</div>
+      <div className='ml-2 mr-0.5 text-text-primary system-sm-regular'>{label}</div>
+      {tooltip && (
+        <Tooltip
+          asChild
+          popupContent={tooltip}
+          popupClassName='w-[200px]'
+        >
+          <div className='size-4 flex items-center justify-center'>
+            <RiQuestionLine className='text-text-quaternary' />
+          </div>
+        </Tooltip>
+      )}
     </div>
   )
 }
 
-const priceClassName = 'leading-[32px] text-[28px] font-bold text-gray-900'
+const priceClassName = 'leading-[125%] text-[28px] font-bold text-text-primary'
 const style = {
   [Plan.sandbox]: {
-    bg: 'bg-[#F2F4F7]',
-    title: 'text-gray-900',
-    hoverAndActive: '',
+    icon: <ArCube1 className='text-text-primary size-7' />,
+    description: 'text-util-colors-gray-gray-600',
+    btnStyle: 'bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover border-[0.5px] border-components-button-secondary-border text-text-primary',
+    btnDisabledStyle: 'bg-components-button-secondary-bg-disabled hover:bg-components-button-secondary-bg-disabled border-components-button-secondary-border-disabled text-components-button-secondary-text-disabled',
   },
   [Plan.professional]: {
-    bg: 'bg-[#E0F2FE]',
-    title: 'text-[#026AA2]',
-    hoverAndActive: 'hover:shadow-lg hover:!text-white hover:!bg-[#0086C9] hover:!border-[#026AA2] active:!text-white active:!bg-[#026AA2] active:!border-[#026AA2]',
+    icon: <Keyframe className='text-util-colors-blue-brand-blue-brand-600 size-7' />,
+    description: 'text-util-colors-blue-brand-blue-brand-600',
+    btnStyle: 'bg-components-button-primary-bg hover:bg-components-button-primary-bg-hover border border-components-button-primary-border text-components-button-primary-text',
+    btnDisabledStyle: 'bg-components-button-primary-bg-disabled hover:bg-components-button-primary-bg-disabled border-components-button-primary-border-disabled text-components-button-primary-text-disabled',
   },
   [Plan.team]: {
-    bg: 'bg-[#E0EAFF]',
-    title: 'text-[#3538CD]',
-    hoverAndActive: 'hover:shadow-lg hover:!text-white hover:!bg-[#444CE7] hover:!border-[#3538CD] active:!text-white active:!bg-[#3538CD] active:!border-[#3538CD]',
-  },
-  [Plan.enterprise]: {
-    bg: 'bg-[#FFEED3]',
-    title: 'text-[#DC6803]',
-    hoverAndActive: 'hover:shadow-lg hover:!text-white hover:!bg-[#F79009] hover:!border-[#DC6803] active:!text-white active:!bg-[#DC6803] active:!border-[#DC6803]',
+    icon: <Group2 className='text-util-colors-indigo-indigo-600 size-7' />,
+    description: 'text-util-colors-indigo-indigo-600',
+    btnStyle: 'bg-components-button-indigo-bg hover:bg-components-button-indigo-bg-hover border border-components-button-primary-border text-components-button-primary-text',
+    btnDisabledStyle: 'bg-components-button-indigo-bg-disabled hover:bg-components-button-indigo-bg-disabled border-components-button-indigo-border-disabled text-components-button-primary-text-disabled',
   },
 }
 const PlanItem: FC<Props> = ({
   plan,
   currentPlan,
   planRange,
-  canPay,
 }) => {
   const { t } = useTranslation()
-  const { locale } = useContext(I18n)
-
-  const isZh = locale === LanguagesSupported[1]
   const [loading, setLoading] = React.useState(false)
   const i18nPrefix = `billing.plans.${plan}`
   const isFreePlan = plan === Plan.sandbox
-  const isEnterprisePlan = plan === Plan.enterprise
   const isMostPopularPlan = plan === Plan.professional
   const planInfo = ALL_PLANS[plan]
   const isYear = planRange === PlanRange.yearly
   const isCurrent = plan === currentPlan
-  const isPlanDisabled = planInfo.level <= ALL_PLANS[currentPlan].level || (!canPay && plan !== Plan.enterprise)
+  const isPlanDisabled = planInfo.level <= ALL_PLANS[currentPlan].level
   const { isCurrentWorkspaceManager } = useAppContext()
-  const messagesRequest = (() => {
-    const value = planInfo.messageRequest[isZh ? 'zh' : 'en']
-    if (value === contractSales)
-      return t('billing.plansCommon.contractSales')
 
-    return value
-  })()
   const btnText = (() => {
-    if (!canPay && plan !== Plan.enterprise)
-      return t('billing.plansCommon.contractOwner')
-
     if (isCurrent)
       return t('billing.plansCommon.currentPlan')
 
     return ({
       [Plan.sandbox]: t('billing.plansCommon.startForFree'),
-      [Plan.professional]: <>{t('billing.plansCommon.getStartedWith')}<span className='capitalize'>&nbsp;{plan}</span></>,
-      [Plan.team]: <>{t('billing.plansCommon.getStartedWith')}<span className='capitalize'>&nbsp;{plan}</span></>,
-      [Plan.enterprise]: t('billing.plansCommon.talkToSales'),
+      [Plan.professional]: t('billing.plansCommon.getStarted'),
+      [Plan.team]: t('billing.plansCommon.getStarted'),
     })[plan]
   })()
-  const comingSoon = (
-    <div className='leading-[12px] text-[9px] font-semibold text-[#3538CD] uppercase'>{t('billing.plansCommon.comingSoon')}</div>
-  )
-  const supportContent = (() => {
-    switch (plan) {
-      case Plan.sandbox:
-        return (<div className='space-y-3.5'>
-          <div>{t('billing.plansCommon.supportItems.communityForums')}</div>
-          <div>{t('billing.plansCommon.supportItems.agentMode')}</div>
-          <div className='flex items-center space-x-1'>
-            <div className='flex items-center'>
-              <div className='mr-0.5'>&nbsp;{t('billing.plansCommon.supportItems.workflow')}</div>
-            </div>
-          </div>
-        </div>)
-      case Plan.professional:
-        return (
-          <div>
-            <div>{t('billing.plansCommon.supportItems.emailSupport')}</div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div>+ {t('billing.plansCommon.supportItems.logoChange')}</div>
-            </div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div>+ {t('billing.plansCommon.supportItems.bulkUpload')}</div>
-            </div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <span>+ </span>
-              <div>{t('billing.plansCommon.supportItems.llmLoadingBalancing')}</div>
-              <Tooltip
-                popupContent={
-                  <div className='w-[200px]'>{t('billing.plansCommon.supportItems.llmLoadingBalancingTooltip')}</div>
-                }
-              />
-            </div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div className='flex items-center'>
-                +
-                <div className='mr-0.5'>&nbsp;{t('billing.plansCommon.supportItems.ragAPIRequest')}</div>
-                <Tooltip
-                  popupContent={
-                    <div className='w-[200px]'>{t('billing.plansCommon.ragAPIRequestTooltip')}</div>
-                  }
-                />
-              </div>
-              <div>{comingSoon}</div>
-            </div>
-          </div>
-        )
-      case Plan.team:
-        return (
-          <div>
-            <div>{t('billing.plansCommon.supportItems.priorityEmail')}</div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div>+ {t('billing.plansCommon.supportItems.SSOAuthentication')}</div>
-              <div>{comingSoon}</div>
-            </div>
-          </div>
-        )
-      case Plan.enterprise:
-        return (
-          <div>
-            <div>{t('billing.plansCommon.supportItems.personalizedSupport')}</div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div>+ {t('billing.plansCommon.supportItems.dedicatedAPISupport')}</div>
-            </div>
-            <div className='mt-3.5 flex items-center space-x-1'>
-              <div>+ {t('billing.plansCommon.supportItems.customIntegration')}</div>
-            </div>
-          </div>
-        )
-      default:
-        return ''
-    }
-  })()
+
   const handleGetPayUrl = async () => {
     if (loading)
       return
@@ -187,10 +101,6 @@ const PlanItem: FC<Props> = ({
     if (isFreePlan)
       return
 
-    if (isEnterprisePlan) {
-      window.location.href = contactSalesUrl
-      return
-    }
     // Only workspace manager can buy plan
     if (!isCurrentWorkspaceManager) {
       Toast.notify({
@@ -211,90 +121,103 @@ const PlanItem: FC<Props> = ({
     }
   }
   return (
-    <div className={cn(isMostPopularPlan ? 'bg-[#0086C9] p-0.5' : 'pt-7', 'flex flex-col min-w-[290px] w-[290px] rounded-xl')}>
-      {isMostPopularPlan && (
-        <div className='flex items-center h-7 justify-center leading-[12px] text-xs font-medium text-[#F5F8FF]'>{t('billing.plansCommon.mostPopular')}</div>
-      )}
-      <div className={cn(style[plan].bg, 'grow px-6 py-6 rounded-[10px]')}>
-        <div className={cn(style[plan].title, 'mb-1 leading-[125%] text-lg font-semibold')}>{t(`${i18nPrefix}.name`)}</div>
-        <div className={cn(isFreePlan ? 'mb-5 text-[#FB6514]' : 'mb-4 text-gray-500', 'h-8 leading-[125%] text-[13px] font-normal')}>{t(`${i18nPrefix}.description`)}</div>
-
+    <div className={cn('flex flex-col w-[373px] p-6 border-[0.5px] border-effects-highlight-lightmode-off bg-background-section-burn rounded-2xl',
+      isMostPopularPlan ? 'shadow-lg backdrop-blur-[5px] border-effects-highlight' : 'hover:shadow-lg hover:backdrop-blur-[5px] hover:border-effects-highlight',
+    )}>
+      <div className='flex flex-col gap-y-1'>
+        {style[plan].icon}
+        <div className='flex items-center'>
+          <div className='leading-[125%] text-lg font-semibold uppercase text-text-primary'>{t(`${i18nPrefix}.name`)}</div>
+          {isMostPopularPlan && <div className='ml-1 px-1 py-[3px] flex items-center justify-center rounded-full border-[0.5px] shadow-xs bg-price-premium-badge-background text-components-premium-badge-grey-text-stop-0'>
+            <div className='pl-0.5'>
+              <SparklesSoft className='size-3' />
+            </div>
+            <span className='px-0.5 system-2xs-semibold-uppercase bg-clip-text bg-price-premium-text-background text-transparent'>{t('billing.plansCommon.mostPopular')}</span>
+          </div>}
+        </div>
+        <div className={cn(style[plan].description, 'system-sm-regular')}>{t(`${i18nPrefix}.description`)}</div>
+      </div>
+      <div className='my-5'>
         {/* Price */}
         {isFreePlan && (
           <div className={priceClassName}>{t('billing.plansCommon.free')}</div>
         )}
-        {isEnterprisePlan && (
-          <div className={priceClassName}>{t('billing.plansCommon.contactSales')}</div>
-        )}
-        {!isFreePlan && !isEnterprisePlan && (
-          <div className='flex items-end h-9'>
+        {!isFreePlan && (
+          <div className='flex items-end'>
             <div className={priceClassName}>${isYear ? planInfo.price * 10 : planInfo.price}</div>
-            <div className='ml-1'>
-              {isYear && <div className='leading-[18px] text-xs font-medium text-[#F26725]'>{t('billing.plansCommon.save')}${planInfo.price * 2}</div>}
-              <div className='leading-[18px] text-[15px] font-normal text-gray-500'>/{t(`billing.plansCommon.${!isYear ? 'month' : 'year'}`)}</div>
+            <div className='ml-1 flex flex-col'>
+              {isYear && <div className='leading-[14px] text-[14px] font-normal italic text-text-warning'>{t('billing.plansCommon.save')}${planInfo.price * 2}</div>}
+              <div className='leading-normal text-[14px] font-normal text-text-tertiary'>
+                {t('billing.plansCommon.priceTip')}
+                {t(`billing.plansCommon.${!isYear ? 'month' : 'year'}`)}</div>
             </div>
           </div>
         )}
+      </div>
 
-        <div
-          className={cn(isMostPopularPlan && !isCurrent && '!bg-[#444CE7] !text-white !border !border-[#3538CD] shadow-sm', isPlanDisabled ? 'opacity-30' : `${style[plan].hoverAndActive} cursor-pointer`, 'mt-4 flex h-11 items-center justify-center border-[2px] border-gray-900 rounded-3xl text-sm font-semibold text-gray-900')}
-          onClick={handleGetPayUrl}
-        >
-          {btnText}
-        </div>
-
-        <div className='my-4 h-[1px] bg-black/5'></div>
-
-        <div className='leading-[125%] text-[13px] font-normal text-gray-900'>
-          {t(`${i18nPrefix}.includesTitle`)}
-        </div>
+      <div
+        className={cn('flex py-3 px-5 rounded-full justify-center items-center h-[42px]',
+          style[plan].btnStyle,
+          isPlanDisabled && style[plan].btnDisabledStyle,
+          isPlanDisabled ? 'cursor-not-allowed' : 'cursor-pointer')}
+        onClick={handleGetPayUrl}
+      >
+        {btnText}
+      </div>
+      <div className='flex flex-col gap-y-3 mt-6'>
         <KeyValue
-          label={t('billing.plansCommon.messageRequest.title')}
-          value={messagesRequest}
+          icon={<RiChatAiLine />}
+          label={isFreePlan
+            ? t('billing.plansCommon.messageRequest.title', { count: planInfo.messageRequest })
+            : t('billing.plansCommon.messageRequest.titlePerMonth', { count: planInfo.messageRequest })}
           tooltip={t('billing.plansCommon.messageRequest.tooltip') as string}
         />
         <KeyValue
+          icon={<RiBrain2Line />}
           label={t('billing.plansCommon.modelProviders')}
-          value={planInfo.modelProviders}
         />
         <KeyValue
-          label={t('billing.plansCommon.teamMembers')}
-          value={planInfo.teamMembers === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : planInfo.teamMembers}
+          icon={<RiFolder6Line />}
+          label={t('billing.plansCommon.teamWorkspace', { count: planInfo.teamWorkspace })}
         />
         <KeyValue
-          label={t('billing.plansCommon.buildApps')}
-          value={planInfo.buildApps === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : planInfo.buildApps}
+          icon={<RiGroupLine />}
+          label={t('billing.plansCommon.teamMember', { count: planInfo.teamMembers })}
         />
         <KeyValue
-          label={t('billing.plansCommon.vectorSpace')}
-          value={planInfo.vectorSpace === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : (planInfo.vectorSpace >= 1000 ? `${planInfo.vectorSpace / 1000}G` : `${planInfo.vectorSpace}MB`)}
-          tooltip={t('billing.plansCommon.vectorSpaceBillingTooltip') as string}
+          icon={<RiApps2Line />}
+          label={t('billing.plansCommon.buildApps', { count: planInfo.buildApps })}
         />
+        <Divider bgStyle='gradient' />
         <KeyValue
-          label={t('billing.plansCommon.documentsUploadQuota')}
-          value={planInfo.vectorSpace === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : planInfo.documentsUploadQuota}
+          icon={<RiBook2Line />}
+          label={t('billing.plansCommon.documents', { count: planInfo.documents })}
+          tooltip={t('billing.plansCommon.documentsTooltip') as string}
         />
         <KeyValue
-          label={t('billing.plansCommon.documentProcessingPriority')}
-          value={t(`billing.plansCommon.priority.${planInfo.documentProcessingPriority}`) as string}
+          icon={<RiHardDrive3Line />}
+          label={t('billing.plansCommon.vectorSpace', { size: planInfo.vectorSpace })}
+          tooltip={t('billing.plansCommon.vectorSpaceTooltip') as string}
         />
 
         <KeyValue
-          label={t('billing.plansCommon.annotatedResponse.title')}
-          value={planInfo.annotatedResponse === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : `${planInfo.annotatedResponse}`}
-          tooltip={t('billing.plansCommon.annotatedResponse.tooltip') as string}
+          icon={<RiSeoLine />}
+          label={t('billing.plansCommon.documentsRequestQuota', { count: planInfo.documentsRequestQuota })}
+          tooltip={t('billing.plansCommon.documentsRequestQuotaTooltip')}
         />
         <KeyValue
-          label={t('billing.plansCommon.logsHistory')}
-          value={planInfo.logHistory === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : `${planInfo.logHistory} ${t('billing.plansCommon.days')}`}
+          icon={<RiProgress3Line />}
+          label={[t(`billing.plansCommon.priority.${planInfo.documentProcessingPriority}`), t('billing.plansCommon.documentProcessingPriority')].join('')}
         />
+        <Divider bgStyle='gradient' />
         <KeyValue
-          label={t('billing.plansCommon.customTools')}
-          value={planInfo.customTools === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : (planInfo.customTools === unAvailable ? t('billing.plansCommon.unavailable') as string : `${planInfo.customTools}`)}
+          icon={<RiFileEditLine />}
+          label={t('billing.plansCommon.annotatedResponse.title', { count: planInfo.annotatedResponse })}
+          tooltip={t('billing.plansCommon.annotatedResponse.tooltip') as string}
         />
         <KeyValue
-          label={t('billing.plansCommon.support')}
-          value={supportContent}
+          icon={<RiHistoryLine />}
+          label={t('billing.plansCommon.logsHistory', { days: planInfo.logHistory === NUM_INFINITE ? t('billing.plansCommon.unlimited') as string : `${planInfo.logHistory} ${t('billing.plansCommon.days')}` })}
         />
       </div>
     </div>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 24 - 25
web/app/components/billing/pricing/select-plan-range.tsx


+ 176 - 0
web/app/components/billing/pricing/self-hosted-plan-item.tsx

@@ -0,0 +1,176 @@
+'use client'
+import type { FC, ReactNode } from 'react'
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import { RiArrowRightUpLine, RiBrain2Line, RiCheckLine, RiQuestionLine } from '@remixicon/react'
+import { SelfHostedPlan } from '../type'
+import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '../config'
+import Toast from '../../base/toast'
+import Tooltip from '../../base/tooltip'
+import { Asterisk, AwsMarketplace, Azure, Buildings, Diamond, GoogleCloud } from '../../base/icons/src/public/billing'
+import type { PlanRange } from './select-plan-range'
+import cn from '@/utils/classnames'
+import { useAppContext } from '@/context/app-context'
+
+type Props = {
+  plan: SelfHostedPlan
+  planRange: PlanRange
+  canPay: boolean
+}
+
+const KeyValue = ({ label, tooltip, textColor, tooltipIconColor }: { icon: ReactNode; label: string; tooltip?: string; textColor: string; tooltipIconColor: string }) => {
+  return (
+    <div className={cn('flex', textColor)}>
+      <div className='size-4 flex items-center justify-center'>
+        <RiCheckLine />
+      </div>
+      <div className={cn('ml-2 mr-0.5 system-sm-regular', textColor)}>{label}</div>
+      {tooltip && (
+        <Tooltip
+          asChild
+          popupContent={tooltip}
+          popupClassName='w-[200px]'
+        >
+          <div className='size-4 flex items-center justify-center'>
+            <RiQuestionLine className={cn(tooltipIconColor)} />
+          </div>
+        </Tooltip>
+      )}
+    </div>
+  )
+}
+
+const style = {
+  [SelfHostedPlan.community]: {
+    icon: <Asterisk className='text-text-primary size-7' />,
+    title: 'text-text-primary',
+    price: 'text-text-primary',
+    priceTip: 'text-text-tertiary',
+    description: 'text-util-colors-gray-gray-600',
+    bg: 'border-effects-highlight-lightmode-off bg-background-section-burn',
+    btnStyle: 'bg-components-button-secondary-bg hover:bg-components-button-secondary-bg-hover border-[0.5px] border-components-button-secondary-border text-text-primary',
+    values: 'text-text-secondary',
+    tooltipIconColor: 'text-text-tertiary',
+  },
+  [SelfHostedPlan.premium]: {
+    icon: <Diamond className='text-text-warning size-7' />,
+    title: 'text-text-primary',
+    price: 'text-text-primary',
+    priceTip: 'text-text-tertiary',
+    description: 'text-text-warning',
+    bg: 'border-effects-highlight bg-background-section-burn',
+    btnStyle: 'bg-third-party-aws hover:bg-third-party-aws-hover border border-components-button-primary-border text-text-primary-on-surface shadow-xs',
+    values: 'text-text-secondary',
+    tooltipIconColor: 'text-text-tertiary',
+  },
+  [SelfHostedPlan.enterprise]: {
+    icon: <Buildings className='text-text-primary-on-surface size-7' />,
+    title: 'text-text-primary-on-surface',
+    price: 'text-text-primary-on-surface',
+    priceTip: 'text-text-primary-on-surface',
+    description: 'text-text-primary-on-surface',
+    bg: 'border-effects-highlight bg-[#155AEF] text-text-primary-on-surface',
+    btnStyle: 'bg-white bg-opacity-96 hover:opacity-85 border-[0.5px] border-components-button-secondary-border text-[#155AEF] shadow-xs',
+    values: 'text-text-primary-on-surface',
+    tooltipIconColor: 'text-text-primary-on-surface',
+  },
+}
+const SelfHostedPlanItem: FC<Props> = ({
+  plan,
+}) => {
+  const { t } = useTranslation()
+  const isFreePlan = plan === SelfHostedPlan.community
+  const isPremiumPlan = plan === SelfHostedPlan.premium
+  const i18nPrefix = `billing.plans.${plan}`
+  const isEnterprisePlan = plan === SelfHostedPlan.enterprise
+  const { isCurrentWorkspaceManager } = useAppContext()
+  const features = t(`${i18nPrefix}.features`, { returnObjects: true }) as string[]
+  const handleGetPayUrl = () => {
+    // Only workspace manager can buy plan
+    if (!isCurrentWorkspaceManager) {
+      Toast.notify({
+        type: 'error',
+        message: t('billing.buyPermissionDeniedTip'),
+        className: 'z-[1001]',
+      })
+      return
+    }
+    if (isFreePlan) {
+      window.location.href = getStartedWithCommunityUrl
+      return
+    }
+    if (isPremiumPlan) {
+      window.location.href = getWithPremiumUrl
+      return
+    }
+
+    if (isEnterprisePlan)
+      window.location.href = contactSalesUrl
+  }
+  return (
+    <div className={cn(`relative flex flex-col w-[374px] border-[0.5px] rounded-2xl
+      hover:shadow-lg hover:backdrop-blur-[5px] hover:border-effects-highlight overflow-hidden`, style[plan].bg)}>
+      <div>
+        <div className={cn(isEnterprisePlan ? 'bg-price-enterprise-background absolute left-0 top-0 right-0 bottom-0 z-1' : '')} >
+        </div>
+        {isEnterprisePlan && <div className='bg-[#09328c] opacity-15 mix-blend-plus-darker blur-[80px] size-[341px] rounded-full absolute -top-[104px] -left-[90px] z-15'></div>}
+        {isEnterprisePlan && <div className='bg-[#e2eafb] opacity-15 mix-blend-plus-darker blur-[80px] size-[341px] rounded-full absolute -right-[40px] -bottom-[72px] z-15'></div>}
+      </div>
+      <div className='relative w-full p-6 z-10 min-h-[559px]'>
+        <div className=' flex flex-col gap-y-1 min-h-[108px]'>
+          {style[plan].icon}
+          <div className='flex items-center'>
+            <div className={cn('leading-[125%] system-md-semibold uppercase', style[plan].title)}>{t(`${i18nPrefix}.name`)}</div>
+          </div>
+          <div className={cn(style[plan].description, 'system-sm-regular')}>{t(`${i18nPrefix}.description`)}</div>
+        </div>
+        <div className='my-3'>
+          <div className='flex items-end'>
+            <div className={cn('leading-[125%] text-[28px] font-bold shrink-0', style[plan].price)}>{t(`${i18nPrefix}.price`)}</div>
+            {!isFreePlan
+              && <span className={cn('ml-2 py-1 leading-normal text-[14px] font-normal', style[plan].priceTip)}>
+                {t(`${i18nPrefix}.priceTip`)}
+              </span>}
+          </div>
+        </div>
+
+        <div
+          className={cn('flex py-3 px-5 rounded-full justify-center items-center h-[44px] system-md-semibold cursor-pointer',
+            style[plan].btnStyle)}
+          onClick={handleGetPayUrl}
+        >
+          {t(`${i18nPrefix}.btnText`)}
+          {isPremiumPlan
+            && <>
+              <div className='pt-[6px] mx-1'>
+                <AwsMarketplace className='h-6' />
+              </div>
+              <RiArrowRightUpLine className='size-4' />
+            </>}
+        </div>
+        <div className={cn('mt-6 system-sm-semibold mb-2', style[plan].values)}>{t(`${i18nPrefix}.includesTitle`)}</div>
+        <div className='flex flex-col gap-y-3'>
+          {features.map(v =>
+            <KeyValue key={`${plan}-${v}`}
+              textColor={style[plan].values}
+              tooltipIconColor={style[plan].tooltipIconColor}
+              icon={<RiBrain2Line />}
+              label={v}
+            />)}
+        </div>
+        {isPremiumPlan && <div className='mt-[68px]'>
+          <div className='flex items-center gap-x-1'>
+            <div className='size-8 flex items-center justify-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default shadow-xs'>
+              <Azure />
+            </div>
+            <div className='size-8 flex items-center justify-center rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default shadow-xs'>
+              <GoogleCloud />
+            </div>
+          </div>
+          <span className={cn('mt-2 system-xs-regular', style[plan].tooltipIconColor)}>{t('billing.plans.premium.comingSoon')}</span>
+        </div>}
+      </div>
+    </div>
+  )
+}
+export default React.memo(SelfHostedPlanItem)

+ 28 - 9
web/app/components/billing/type.ts

@@ -2,9 +2,7 @@ export enum Plan {
   sandbox = 'sandbox',
   professional = 'professional',
   team = 'team',
-  enterprise = 'enterprise',
 }
-
 export enum Priority {
   standard = 'standard',
   priority = 'priority',
@@ -14,21 +12,42 @@ export type PlanInfo = {
   level: number
   price: number
   modelProviders: string
+  teamWorkspace: number
   teamMembers: number
   buildApps: number
-  vectorSpace: number
+  documents: number
+  vectorSpace: string
   documentsUploadQuota: number
+  documentsRequestQuota: number
   documentProcessingPriority: Priority
   logHistory: number
-  customTools: string | number
-  messageRequest: {
-    en: string | number
-    zh: string | number
-  }
+  messageRequest: number
+  annotatedResponse: number
+}
+
+export enum SelfHostedPlan {
+  community = 'community',
+  premium = 'premium',
+  enterprise = 'enterprise',
+}
+
+export type SelfHostedPlanInfo = {
+  level: number
+  price: number
+  modelProviders: string
+  teamWorkspace: number
+  teamMembers: number
+  buildApps: number
+  documents: number
+  vectorSpace: string
+  documentsRequestQuota: number
+  documentProcessingPriority: Priority
+  logHistory: number
+  messageRequest: number
   annotatedResponse: number
 }
 
-export type UsagePlanInfo = Pick<PlanInfo, 'vectorSpace' | 'buildApps' | 'teamMembers' | 'annotatedResponse' | 'documentsUploadQuota'>
+export type UsagePlanInfo = Pick<PlanInfo, 'buildApps' | 'teamMembers' | 'annotatedResponse' | 'documentsUploadQuota'> & { vectorSpace: number }
 
 export enum DocumentProcessingPriority {
   standard = 'standard',

+ 1 - 1
web/app/components/billing/usage-info/apps-info.tsx

@@ -23,7 +23,7 @@ const AppsInfo: FC<Props> = ({
     <UsageInfo
       className={className}
       Icon={ChatBot}
-      name={t('billing.plansCommon.buildApps')}
+      name={t('billing.usagePage.buildApps')}
       usage={usage.buildApps}
       total={total.buildApps}
     />

+ 2 - 2
web/app/components/billing/usage-info/vector-space-info.tsx

@@ -23,8 +23,8 @@ const VectorSpaceInfo: FC<Props> = ({
     <UsageInfo
       className={className}
       Icon={ArtificialBrain}
-      name={t('billing.plansCommon.vectorSpace')}
-      tooltip={t('billing.plansCommon.vectorSpaceTooltip') as string}
+      name={t('billing.usagePage.vectorSpace')}
+      tooltip={t('billing.usagePage.vectorSpaceTooltip') as string}
       usage={usage.vectorSpace}
       total={total.vectorSpace}
       unit='MB'

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 87 - 29
web/i18n/en-US/billing.ts


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 90 - 33
web/i18n/ja-JP/billing.ts


+ 85 - 28
web/i18n/zh-Hans/billing.ts

@@ -1,5 +1,13 @@
 const translation = {
   currentPlan: '当前套餐',
+  usagePage: {
+    teamMembers: '团队成员',
+    buildApps: '构建应用程序数',
+    annotationQuota: '标注回复数',
+    documentsUploadQuota: '文档上传配额',
+    vectorSpace: '知识库数据存储空间',
+    vectorSpaceTooltip: '采用高质量索引模式的文档会消耗知识数据存储资源。当知识数据存储达到限制时,将不会上传新文档。',
+  },
   upgradeBtn: {
     plain: '升级套餐',
     encourage: '立即升级',
@@ -8,9 +16,14 @@ const translation = {
   viewBilling: '管理账单及订阅',
   buyPermissionDeniedTip: '请联系企业管理员订阅',
   plansCommon: {
-    title: '选择适合您的套餐',
-    yearlyTip: '订阅年度计划可免费获得 2个月!',
+    title: '为您的 AI 之旅提供动力的定价套餐',
+    freeTrialTipPrefix: '注册即可',
+    freeTrialTip: '免费试用 200 个 OpenAI 消息额度',
+    freeTrialTipSuffix: '。无需信用卡',
+    yearlyTip: '支付 10 个月,享受 1 年!',
     mostPopular: '最受欢迎',
+    cloud: '云服务',
+    self: '自部署',
     planRange: {
       monthly: '按月',
       yearly: '按年',
@@ -18,31 +31,36 @@ const translation = {
     month: '月',
     year: '年',
     save: '节省',
+    free: '免费',
+    annualBilling: '按年计费',
+    comparePlanAndFeatures: '对比套餐 & 功能特性',
+    priceTip: '每个团队空间/',
     currentPlan: '当前计划',
     contractSales: '联系销售',
     contractOwner: '联系团队管理员',
-    free: '免费',
     startForFree: '免费开始',
-    getStartedWith: '开始使用',
+    getStarted: '立即开始',
     contactSales: '联系销售',
     talkToSales: '联系销售',
-    modelProviders: '支持的模型提供商',
-    teamMembers: '团队成员',
-    buildApps: '构建应用程序数',
+    modelProviders: '支持 OpenAI/Anthropic/Llama2/Azure OpenAI/Hugging Face/Replicate',
+    teamWorkspace: '{{count,number}} 个团队空间',
+    teamMember_one: '{{count,number}} 名团队成员',
+    teamMember_other: '{{count,number}} 名团队成员',
     annotationQuota: '标注回复数',
-    vectorSpace: '向量空间',
-    vectorSpaceTooltip: '向量空间是 LLMs 理解您的数据所需的长期记忆系统。',
-    vectorSpaceBillingTooltip: '向量存储是将知识库向量化处理后为让 LLMs 理解数据而使用的长期记忆存储,1MB 大约能满足1.2 million character 的向量化后数据存储(以 OpenAI Embedding 模型估算,不同模型计算方式有差异)。在向量化过程中,实际的压缩或尺寸减小取决于内容的复杂性和冗余性。',
-    documentsUploadQuota: '文档上传配额',
-    documentProcessingPriority: '文档处理优先级',
-    documentProcessingPriorityTip: '如需更高的文档处理优先级,请升级您的套餐',
-    documentProcessingPriorityUpgrade: '以更快的速度、更高的精度处理更多的数据。',
+    buildApps: '{{count, number}} 个应用程序',
+    documents: '{{count, number}} 个知识库文档上传配额',
+    documentsTooltip: '从知识库的数据源导入的文档数量配额。',
+    vectorSpace: '{{size}} 知识库数据存储空间',
+    vectorSpaceTooltip: '采用高质量索引模式的文档会消耗知识数据存储资源。当知识数据存储达到限制时,将不会上传新文档。',
+    documentsRequestQuota: '{{count,number}}/分钟 知识库请求频率限制',
+    documentsRequestQuotaTooltip: '指每分钟内,一个空间在知识库中可执行的操作总数,包括数据集的创建、删除、更新,文档的上传、修改、归档,以及知识库查询等,用于评估知识库请求的性能。例如,Sandbox 用户在 1 分钟内连续执行 10 次命中测试,其工作区将在接下来的 1 分钟内无法继续执行以下操作:数据集的创建、删除、更新,文档的上传、修改等操作。',
+    documentProcessingPriority: '文档处理',
     priority: {
       'standard': '标准',
       'priority': '优先',
       'top-priority': '最高优先级',
     },
-    logsHistory: '日志历史',
+    logsHistory: '{{days}}日志历史',
     customTools: '自定义工具',
     unavailable: '不可用',
     days: '天',
@@ -68,12 +86,13 @@ const translation = {
     member: '成员',
     memberAfter: '个成员',
     messageRequest: {
-      title: '消息额度',
-      tooltip: '为不同方案提供基于 OpenAI 模型的消息响应额度。',
+      title: '{{count,number}} 条消息额度',
+      titlePerMonth: '{{count,number}} 条消息额度/月',
+      tooltip: '为不同方案提供基于OpenAl模型的消息响应额度。',
     },
     annotatedResponse: {
-      title: '标注回复数',
-      tooltip: '标注回复功能通过人工编辑标注为应用提供了可定制的高质量问答回复能力',
+      title: '{{count,number}} 个标注回复数',
+      tooltip: '标注回复功能通过人工编辑标注为应用提供了可定制的高质量问答回复能力',
     },
     ragAPIRequestTooltip: '指单独调用 Dify 知识库数据处理能力的 API。',
     receiptInfo: '只有团队所有者和团队管理员才能订阅和查看账单信息',
@@ -81,27 +100,65 @@ const translation = {
   plans: {
     sandbox: {
       name: 'Sandbox',
-      description: '200次 GPT 免费试用',
-      includesTitle: '包括:',
+      description: '核心功能免费试用',
     },
     professional: {
       name: 'Professional',
-      description: '让个人和小团队能够以经济实惠的方式释放更多能力。',
-      includesTitle: 'Sandbox 计划中的一切,加上:',
+      description: '对于独立开发者/小团队',
     },
     team: {
       name: 'Team',
-      description: '协作无限制并享受顶级性能。',
-      includesTitle: 'Professional 计划中的一切,加上:',
+      description: '对于中型团队',
+    },
+    community: {
+      name: 'Community',
+      description: '适用于个人用户、小型团队或非商业项目',
+      price: '免费',
+      btnText: '开始使用',
+      includesTitle: '免费功能:',
+      features: [
+        '所有核心功能均在公共存储库下发布',
+        '单一工作空间',
+        '符合 Dify 开源许可证',
+      ],
+    },
+    premium: {
+      name: 'Premium',
+      description: '对于中型组织和团队',
+      price: '可扩展',
+      priceTip: '基于云市场',
+      btnText: '获得 Premium 版',
+      includesTitle: 'Community 版的所有功能,加上:',
+      comingSoon: '即将支持 Microsoft Azure & Google Cloud',
+      features: [
+        '各个云提供商自行管理的可靠性',
+        '单一工作空间',
+        '自定义 WebApp & 品牌',
+        '优先电子邮件 & 聊天支持',
+      ],
     },
     enterprise: {
       name: 'Enterprise',
-      description: '获得大规模关键任务系统的完整功能和支持。',
-      includesTitle: 'Team 计划中的一切,加上:',
+      description: '对于需要组织范围内的安全性、合规性、可扩展性、控制和更高级功能的企业',
+      price: '定制',
+      priceTip: '仅按年计费',
+      btnText: '联系销售',
+      includesTitle: 'Premium 版的所有功能,加上:',
+      features: [
+        '企业级可扩展部署解决方案',
+        '商业许可授权',
+        '专属企业级功能',
+        '多个工作空间 & 企业级管理',
+        'SSO',
+        '由 Dify 合作伙伴支持的可协商的 SLAs',
+        '高级的安全 & 控制',
+        '由 Dify 官方提供的更新 & 维护',
+        '专业技术支持',
+      ],
     },
   },
   vectorSpace: {
-    fullTip: '向量空间已满。',
+    fullTip: '知识库数据存储空间已满。',
     fullSolution: '升级您的套餐以获得更多空间。',
   },
   apps: {

+ 5 - 0
web/tailwind-common-config.ts

@@ -113,6 +113,11 @@ const config = {
         'dataset-option-card-purple-gradient': 'var(--color-dataset-option-card-purple-gradient)',
         'dataset-option-card-orange-gradient': 'var(--color-dataset-option-card-orange-gradient)',
         'dataset-chunk-list-mask-bg': 'var(--color-dataset-chunk-list-mask-bg)',
+        'price-premium-badge-background': 'var(--color-premium-badge-background)',
+        'premium-yearly-tip-text-background': 'var(--color-premium-yearly-tip-text-background)',
+        'price-premium-text-background': 'var(--color-premium-text-background)',
+        'price-enterprise-background': 'var(--color-price-enterprise-background)',
+        'grid-mask-background': 'var(--color-grid-mask-background)',
       },
       animation: {
         'spin-slow': 'spin 2s linear infinite',

+ 61 - 62
web/themes/manual-dark.css

@@ -1,64 +1,63 @@
 html[data-theme="dark"] {
-  --color-chatbot-bg: linear-gradient(180deg,
-      rgba(34, 34, 37, 0.9) 0%,
-      rgba(29, 29, 32, 0.9) 90.48%);
-  --color-chat-bubble-bg: linear-gradient(180deg,
-      rgba(200, 206, 218, 0.08) 0%,
-      rgba(200, 206, 218, 0.02) 100%);
-  --color-chat-input-mask: linear-gradient(180deg,
-      rgba(24, 24, 27, 0.04) 0%,
-      rgba(24, 24, 27, 0.60) 100%);
-  --color-workflow-process-bg: linear-gradient(90deg,
-      rgba(24, 24, 27, 0.25) 0%,
-      rgba(24, 24, 27, 0.04) 100%);
-  --color-workflow-run-failed-bg: linear-gradient(98deg,
-      rgba(240, 68, 56, 0.12) 0%,
-      rgba(0, 0, 0, 0) 26.01%);
-  --color-workflow-batch-failed-bg: linear-gradient(92deg,
-      rgba(240, 68, 56, 0.3) 0%,
-      rgba(0, 0, 0, 0) 100%);
-  --color-marketplace-divider-bg: linear-gradient(90deg,
-      rgba(200, 206, 218, 0.14) 0%,
-      rgba(0, 0, 0, 0) 100%);
-  --color-marketplace-plugin-empty: linear-gradient(180deg,
-      rgba(0, 0, 0, 0) 0%,
-      #222225 100%);
-  --color-toast-success-bg: linear-gradient(92deg,
-      rgba(23, 178, 106, 0.3) 0%,
-      rgba(0, 0, 0, 0) 100%);
-  --color-toast-warning-bg: linear-gradient(92deg,
-      rgba(247, 144, 9, 0.3) 0%,
-      rgba(0, 0, 0, 0) 100%);
-  --color-toast-error-bg: linear-gradient(92deg,
-      rgba(240, 68, 56, 0.3) 0%,
-      rgba(0, 0, 0, 0) 100%);
-  --color-toast-info-bg: linear-gradient(92deg,
-      rgba(11, 165, 236, 0.3) 0%),
-  --color-account-teams-bg: linear-gradient(271deg,
-    rgba(34, 34, 37, 0.9) -0.1%,
-    rgba(29, 29, 32, 0.9) 98.26%
-  );
-  --color-app-detail-bg: linear-gradient(
-    169deg,
-    #1D1D20 1.18%,
-    #222225 99.52%
-  );
-  --color-app-detail-overlay-bg: linear-gradient(
-    270deg,
-    rgba(0, 0, 0, 0.00) 0%,
-    rgba(24, 24, 27, 0.02) 8%,
-    rgba(24, 24, 27, 0.54) 100%
-  );
-  --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
-  --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
-  --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%);
-  --color-dataset-child-chunk-expand-btn-bg: linear-gradient(90deg, rgba(24, 24, 27, 0.25) 0%, rgba(24, 24, 27, 0.04) 100%);
-  --color-dataset-option-card-blue-gradient: linear-gradient(90deg, #24252E 0%, #1E1E21 100%);
-  --color-dataset-option-card-purple-gradient: linear-gradient(90deg, #25242E 0%, #1E1E21 100%);
-  --color-dataset-option-card-orange-gradient: linear-gradient(90deg, #2B2322 0%, #1E1E21 100%);
-  --color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.00) 0%, #222225 100%);
-  --mask-top2bottom-gray-50-to-transparent: linear-gradient(
-    180deg,
-    rgba(24, 24, 27, 0.08) 0%,
-    rgba(0, 0, 0, 0) 100%);
+        --color-premium-yearly-tip-text-background: linear-gradient(91deg, #FDB022 2.18%, #F79009 108.79%);
+        --color-premium-badge-background: linear-gradient(95deg, rgba(103, 111, 131, 0.90) 0%, rgba(73, 84, 100, 0.90) 105.58%), var(--util-colors-gray-gray-200, #18222F);
+        --color-premium-text-background: linear-gradient(92deg, rgba(249, 250, 251, 0.95) 0%, rgba(233, 235, 240, 0.95) 97.78%);
+        --color-price-enterprise-background: linear-gradient(180deg, rgba(185, 211, 234, 0.00) 0%, rgba(180, 209, 234, 0.92) 100%);
+        --color-grid-mask-background: linear-gradient(0deg, rgba(0, 0, 0, 0.00) 0%, rgba(24, 24, 25, 0.1) 62.25%, rgba(24, 24, 25, 0.10) 100%);
+        --color-chatbot-bg: linear-gradient(180deg,
+                        rgba(34, 34, 37, 0.9) 0%,
+                        rgba(29, 29, 32, 0.9) 90.48%);
+        --color-chat-bubble-bg: linear-gradient(180deg,
+                        rgba(200, 206, 218, 0.08) 0%,
+                        rgba(200, 206, 218, 0.02) 100%);
+        --color-chat-input-mask: linear-gradient(180deg,
+                        rgba(24, 24, 27, 0.04) 0%,
+                        rgba(24, 24, 27, 0.60) 100%);
+        --color-workflow-process-bg: linear-gradient(90deg,
+                        rgba(24, 24, 27, 0.25) 0%,
+                        rgba(24, 24, 27, 0.04) 100%);
+        --color-workflow-run-failed-bg: linear-gradient(98deg,
+                        rgba(240, 68, 56, 0.12) 0%,
+                        rgba(0, 0, 0, 0) 26.01%);
+        --color-workflow-batch-failed-bg: linear-gradient(92deg,
+                        rgba(240, 68, 56, 0.3) 0%,
+                        rgba(0, 0, 0, 0) 100%);
+        --color-marketplace-divider-bg: linear-gradient(90deg,
+                        rgba(200, 206, 218, 0.14) 0%,
+                        rgba(0, 0, 0, 0) 100%);
+        --color-marketplace-plugin-empty: linear-gradient(180deg,
+                        rgba(0, 0, 0, 0) 0%,
+                        #222225 100%);
+        --color-toast-success-bg: linear-gradient(92deg,
+                        rgba(23, 178, 106, 0.3) 0%,
+                        rgba(0, 0, 0, 0) 100%);
+        --color-toast-warning-bg: linear-gradient(92deg,
+                        rgba(247, 144, 9, 0.3) 0%,
+                        rgba(0, 0, 0, 0) 100%);
+        --color-toast-error-bg: linear-gradient(92deg,
+                        rgba(240, 68, 56, 0.3) 0%,
+                        rgba(0, 0, 0, 0) 100%);
+        --color-toast-info-bg: linear-gradient(92deg,
+                        rgba(11, 165, 236, 0.3) 0%),
+                --color-account-teams-bg: linear-gradient(271deg,
+                        rgba(34, 34, 37, 0.9) -0.1%,
+                        rgba(29, 29, 32, 0.9) 98.26%);
+        --color-app-detail-bg: linear-gradient(169deg,
+                        #1D1D20 1.18%,
+                        #222225 99.52%);
+        --color-app-detail-overlay-bg: linear-gradient(270deg,
+                        rgba(0, 0, 0, 0.00) 0%,
+                        rgba(24, 24, 27, 0.02) 8%,
+                        rgba(24, 24, 27, 0.54) 100%);
+        --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
+        --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
+        --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%);
+        --color-dataset-child-chunk-expand-btn-bg: linear-gradient(90deg, rgba(24, 24, 27, 0.25) 0%, rgba(24, 24, 27, 0.04) 100%);
+        --color-dataset-option-card-blue-gradient: linear-gradient(90deg, #24252E 0%, #1E1E21 100%);
+        --color-dataset-option-card-purple-gradient: linear-gradient(90deg, #25242E 0%, #1E1E21 100%);
+        --color-dataset-option-card-orange-gradient: linear-gradient(90deg, #2B2322 0%, #1E1E21 100%);
+        --color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.00) 0%, #222225 100%);
+        --mask-top2bottom-gray-50-to-transparent: linear-gradient(180deg,
+                        rgba(24, 24, 27, 0.08) 0%,
+                        rgba(0, 0, 0, 0) 100%);
 }

+ 61 - 62
web/themes/manual-light.css

@@ -1,64 +1,63 @@
 html[data-theme="light"] {
-  --color-chatbot-bg: linear-gradient(180deg,
-      rgba(249, 250, 251, 0.9) 0%,
-      rgba(242, 244, 247, 0.9) 90.48%);
-  --color-chat-bubble-bg: linear-gradient(180deg,
-    #fff 0%,
-    rgba(255, 255, 255, 0.6) 100%);
-  --color-chat-input-mask: linear-gradient(180deg,
-    rgba(255, 255, 255, 0.01) 0%,
-    #F2F4F7 100%);
-  --color-workflow-process-bg: linear-gradient(90deg,
-      rgba(200, 206, 218, 0.2) 0%,
-      rgba(200, 206, 218, 0.04) 100%);
-  --color-workflow-run-failed-bg: linear-gradient(98deg,
-      rgba(240, 68, 56, 0.10) 0%,
-      rgba(255, 255, 255, 0) 26.01%);
-  --color-workflow-batch-failed-bg: linear-gradient(92deg,
-      rgba(240, 68, 56, 0.25) 0%,
-      rgba(255, 255, 255, 0) 100%);
-  --color-marketplace-divider-bg: linear-gradient(90deg,
-      rgba(16, 24, 40, 0.08) 0%,
-      rgba(255, 255, 255, 0) 100%);
-  --color-marketplace-plugin-empty: linear-gradient(180deg,
-      rgba(255, 255, 255, 0) 0%,
-      #fcfcfd 100%);
-  --color-toast-success-bg: linear-gradient(92deg,
-      rgba(23, 178, 106, 0.25) 0%,
-      rgba(255, 255, 255, 0) 100%);
-  --color-toast-warning-bg: linear-gradient(92deg,
-      rgba(247, 144, 9, 0.25) 0%,
-      rgba(255, 255, 255, 0) 100%);
-  --color-toast-error-bg: linear-gradient(92deg,
-      rgba(240, 68, 56, 0.25) 0%,
-      rgba(255, 255, 255, 0) 100%);
-  --color-toast-info-bg: linear-gradient(92deg,
-      rgba(11, 165, 236, 0.25) 0%),
-  --color-account-teams-bg: linear-gradient(271deg,
-    rgba(249, 250, 251, 0.9) -0.1%,
-    rgba(242, 244, 247, 0.9) 98.26%
-  );
-  --color-app-detail-bg: linear-gradient(
-    169deg,
-    #F2F4F7 1.18%,
-    #F9FAFB 99.52%
-  );
-  --color-app-detail-overlay-bg: linear-gradient(
-    270deg,
-    rgba(0, 0, 0, 0.00) 0%,
-    rgba(16, 24, 40, 0.01) 8%,
-    rgba(16, 24, 40, 0.18) 100%
-  );
-  --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
-  --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
-  --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%);
-  --color-dataset-child-chunk-expand-btn-bg: linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%);
-  --color-dataset-option-card-blue-gradient: linear-gradient(90deg, #F2F4F7 0%, #F9FAFB 100%);
-  --color-dataset-option-card-purple-gradient: linear-gradient(90deg, #F0EEFA 0%, #F9FAFB 100%);
-  --color-dataset-option-card-orange-gradient: linear-gradient(90deg, #F8F2EE 0%, #F9FAFB 100%);
-  --color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.00) 0%, #FCFCFD 100%);
-  --mask-top2bottom-gray-50-to-transparent: linear-gradient(
-    180deg,
-    rgba(200, 206, 218, 0.2) 0%,
-    rgba(255, 255, 255, 0) 100%);
+    --color-chatbot-bg: linear-gradient(180deg,
+            rgba(249, 250, 251, 0.9) 0%,
+            rgba(242, 244, 247, 0.9) 90.48%);
+    --color-chat-bubble-bg: linear-gradient(180deg,
+            #fff 0%,
+            rgba(255, 255, 255, 0.6) 100%);
+    --color-workflow-process-bg: linear-gradient(90deg,
+            rgba(200, 206, 218, 0.2) 0%,
+            rgba(200, 206, 218, 0.04) 100%);
+    --color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
+    --color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
+    --color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%);
+    --color-dataset-child-chunk-expand-btn-bg: linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%);
+    --color-dataset-option-card-blue-gradient: linear-gradient(90deg, #F2F4F7 0%, #F9FAFB 100%);
+    --color-dataset-option-card-purple-gradient: linear-gradient(90deg, #F0EEFA 0%, #F9FAFB 100%);
+    --color-dataset-option-card-orange-gradient: linear-gradient(90deg, #F8F2EE 0%, #F9FAFB 100%);
+    --color-dataset-chunk-list-mask-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.00) 0%, #FCFCFD 100%);
+    --mask-top2bottom-gray-50-to-transparent: linear-gradient(180deg,
+            rgba(200, 206, 218, 0.2) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-premium-yearly-tip-text-background: linear-gradient(91deg, #F79009 2.18%, #DC6803 108.79%);
+    --color-premium-badge-background: linear-gradient(95deg, rgba(152, 162, 178, 0.90) 0%, rgba(103, 111, 131, 0.90) 105.58%);
+    --color-premium-text-background: linear-gradient(92deg, rgba(252, 252, 253, 0.95) 0%, rgba(242, 244, 247, 0.95) 97.78%);
+    --color-price-enterprise-background: linear-gradient(180deg, rgba(185, 211, 234, 0.00) 0%, rgba(180, 209, 234, 0.92) 100%);
+    --color-grid-mask-background: linear-gradient(0deg, #FFF 0%, rgba(217, 217, 217, 0.10) 62.25%, rgba(217, 217, 217, 0.10) 100%);
+    --color-chat-input-mask: linear-gradient(180deg,
+            rgba(255, 255, 255, 0.01) 0%,
+            #F2F4F7 100%);
+    --color-workflow-run-failed-bg: linear-gradient(98deg,
+            rgba(240, 68, 56, 0.10) 0%,
+            rgba(255, 255, 255, 0) 26.01%);
+    --color-workflow-batch-failed-bg: linear-gradient(92deg,
+            rgba(240, 68, 56, 0.25) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-marketplace-divider-bg: linear-gradient(90deg,
+            rgba(16, 24, 40, 0.08) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-marketplace-plugin-empty: linear-gradient(180deg,
+            rgba(255, 255, 255, 0) 0%,
+            #fcfcfd 100%);
+    --color-toast-success-bg: linear-gradient(92deg,
+            rgba(23, 178, 106, 0.25) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-toast-warning-bg: linear-gradient(92deg,
+            rgba(247, 144, 9, 0.25) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-toast-error-bg: linear-gradient(92deg,
+            rgba(240, 68, 56, 0.25) 0%,
+            rgba(255, 255, 255, 0) 100%);
+    --color-toast-info-bg: linear-gradient(92deg,
+            rgba(11, 165, 236, 0.25) 0%);
+    --color-account-teams-bg: linear-gradient(271deg,
+            rgba(249, 250, 251, 0.9) -0.1%,
+            rgba(242, 244, 247, 0.9) 98.26%);
+    --color-app-detail-bg: linear-gradient(169deg,
+            #F2F4F7 1.18%,
+            #F9FAFB 99.52%);
+    --color-app-detail-overlay-bg: linear-gradient(270deg,
+            rgba(0, 0, 0, 0.00) 0%,
+            rgba(16, 24, 40, 0.01) 8%,
+            rgba(16, 24, 40, 0.18) 100%);
 }