Initial commit

This commit is contained in:
John Wang
2023-05-15 08:51:32 +08:00
commit db896255d6
744 changed files with 56028 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
import React from 'react'
type Props = {}
const index = (props: Props) => {
return (
<div>index</div>
)
}
export default index

View File

@@ -0,0 +1,16 @@
<svg width="464" height="180" viewBox="0 0 464 180" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="464" height="180" rx="12" fill="#F9FAFB"/>
<rect y="6" width="464" height="8" rx="3" fill="#EAECF0"/>
<rect y="26" width="464" height="8" rx="3" fill="#EAECF0"/>
<rect y="46" width="464" height="8" rx="3" fill="#EAECF0"/>
<rect y="66" width="464" height="8" rx="3" fill="#EAECF0"/>
<rect y="86" width="464" height="8" rx="3" fill="#EAECF0"/>
<rect y="106" width="256" height="8" rx="3" fill="#EAECF0"/>
<path d="M0 60H464V168C464 174.627 458.627 180 452 180H12C5.3726 180 0 174.627 0 168V60Z" fill="url(#paint0_linear_2131_10881)"/>
<defs>
<linearGradient id="paint0_linear_2131_10881" x1="232" y1="60" x2="232" y2="180" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCFCFD" stop-opacity="0"/>
<stop offset="0.741486" stop-color="#FCFCFD"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 892 B

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.40616 0.834307C6.14751 0.719294 5.85222 0.719294 5.59356 0.834307C5.3938 0.923133 5.26403 1.07959 5.17373 1.20708C5.08495 1.33242 4.9899 1.49664 4.88536 1.67723L0.751783 8.81705C0.646828 8.9983 0.551451 9.16302 0.486781 9.3028C0.421056 9.44487 0.349754 9.63584 0.372478 9.85381C0.401884 10.1359 0.549654 10.3922 0.779012 10.5589C0.956259 10.6878 1.15726 10.7218 1.31314 10.7361C1.46651 10.7501 1.65684 10.7501 1.86628 10.7501H10.1334C10.3429 10.7501 10.5332 10.7501 10.6866 10.7361C10.8425 10.7218 11.0435 10.6878 11.2207 10.5589C11.4501 10.3922 11.5978 10.1359 11.6272 9.85381C11.65 9.63584 11.5787 9.44487 11.5129 9.3028C11.4483 9.16303 11.3529 8.99833 11.248 8.81709L7.11436 1.67722C7.00983 1.49663 6.91477 1.33242 6.82599 1.20708C6.73569 1.07959 6.60593 0.923133 6.40616 0.834307ZM6.49988 4.50012C6.49988 4.22398 6.27602 4.00012 5.99988 4.00012C5.72374 4.00012 5.49988 4.22398 5.49988 4.50012V6.50012C5.49988 6.77626 5.72374 7.00012 5.99988 7.00012C6.27602 7.00012 6.49988 6.77626 6.49988 6.50012V4.50012ZM5.99988 8.00012C5.72374 8.00012 5.49988 8.22398 5.49988 8.50012C5.49988 8.77626 5.72374 9.00012 5.99988 9.00012H6.00488C6.28102 9.00012 6.50488 8.77626 6.50488 8.50012C6.50488 8.22398 6.28102 8.00012 6.00488 8.00012H5.99988Z" fill="#F79009"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2413 2H7.7587C6.95374 1.99999 6.28937 1.99998 5.74818 2.04419C5.18608 2.09012 4.66937 2.18868 4.18404 2.43598C3.43139 2.81947 2.81947 3.43139 2.43598 4.18404C2.18868 4.66937 2.09012 5.18608 2.04419 5.74818C1.99998 6.28937 1.99999 6.95372 2 7.75869V13.5343C1.99999 14.2041 1.99999 14.7569 2.03087 15.2095C2.06289 15.6788 2.13142 16.1129 2.30448 16.5307C2.71046 17.5108 3.48916 18.2895 4.46927 18.6955C4.88708 18.8686 5.32118 18.9371 5.79046 18.9691C6.24307 19 6.79594 19 7.46573 19H7.5C8.03656 19 8.14307 19.0063 8.22975 19.0268C8.38085 19.0624 8.52156 19.1328 8.64075 19.2322C8.70913 19.2893 8.77806 19.3708 9.1 19.8L10.5769 21.7692C10.6703 21.8938 10.7758 22.0346 10.8774 22.1476C10.9894 22.2721 11.1756 22.4555 11.4563 22.5647C11.806 22.7007 12.194 22.7007 12.5437 22.5647C12.8244 22.4555 13.0106 22.2721 13.1226 22.1476C13.2242 22.0346 13.3297 21.8938 13.4231 21.7692L14.9 19.8C15.2219 19.3708 15.2909 19.2893 15.3593 19.2322C15.4784 19.1328 15.6192 19.0624 15.7702 19.0268C15.8569 19.0063 15.9634 19 16.5 19H16.5343C17.2041 19 17.7569 19 18.2095 18.9691C18.6788 18.9371 19.1129 18.8686 19.5307 18.6955C20.5108 18.2895 21.2895 17.5108 21.6955 16.5307C21.8686 16.1129 21.9371 15.6788 21.9691 15.2095C22 14.7569 22 14.2041 22 13.5343V7.75868C22 6.95372 22 6.28937 21.9558 5.74818C21.9099 5.18608 21.8113 4.66937 21.564 4.18404C21.1805 3.43139 20.5686 2.81947 19.816 2.43598C19.3306 2.18868 18.8139 2.09012 18.2518 2.04419C17.7106 1.99998 17.0463 1.99999 16.2413 2ZM12 6C11.4477 6 11 6.44772 11 7C11 7.55229 11.4477 8 12 8H12.01C12.5623 8 13.01 7.55229 13.01 7C13.01 6.44772 12.5623 6 12.01 6H12ZM13 10.5C13 9.94772 12.5523 9.5 12 9.5C11.4477 9.5 11 9.94772 11 10.5V14C11 14.5523 11.4477 15 12 15C12.5523 15 13 14.5523 13 14V10.5Z" fill="#155EEF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3332 8H2.6665M2.6665 8L6.6665 12M2.6665 8L6.6665 4" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.12" d="M1.33337 4.13333C1.33337 3.3866 1.33337 3.01323 1.4787 2.72801C1.60653 2.47713 1.8105 2.27316 2.06139 2.14532C2.3466 2 2.71997 2 3.46671 2H3.73337C5.22685 2 5.97358 2 6.54401 2.29065C7.04578 2.54631 7.45373 2.95426 7.70939 3.45603C8.00004 4.02646 8.00004 4.77319 8.00004 6.26667V14L7.93334 13.8999C7.47024 13.2053 7.2387 12.858 6.93277 12.6065C6.66195 12.3839 6.34988 12.2169 6.01444 12.1151C5.63554 12 5.21811 12 4.38326 12H3.46671C2.71997 12 2.3466 12 2.06139 11.8547C1.8105 11.7268 1.60653 11.5229 1.4787 11.272C1.33337 10.9868 1.33337 10.6134 1.33337 9.86667V4.13333Z" fill="#155EEF"/>
<path d="M8.00004 14L7.93334 13.8999C7.47024 13.2053 7.2387 12.858 6.93278 12.6065C6.66195 12.3839 6.34988 12.2169 6.01444 12.1151C5.63554 12 5.21811 12 4.38326 12H3.46671C2.71997 12 2.3466 12 2.06139 11.8547C1.8105 11.7268 1.60653 11.5229 1.4787 11.272C1.33337 10.9868 1.33337 10.6134 1.33337 9.86667V4.13333C1.33337 3.3866 1.33337 3.01323 1.4787 2.72801C1.60653 2.47713 1.8105 2.27316 2.06139 2.14532C2.3466 2 2.71997 2 3.46671 2H3.73337C5.22685 2 5.97358 2 6.54402 2.29065C7.04578 2.54631 7.45373 2.95426 7.70939 3.45603C8.00004 4.02646 8.00004 4.77319 8.00004 6.26667M8.00004 14V6.26667M8.00004 14L8.06674 13.8999C8.52984 13.2053 8.76139 12.858 9.06731 12.6065C9.33814 12.3839 9.6502 12.2169 9.98564 12.1151C10.3645 12 10.782 12 11.6168 12H12.5334C13.2801 12 13.6535 12 13.9387 11.8547C14.1896 11.7268 14.3936 11.5229 14.5214 11.272C14.6667 10.9868 14.6667 10.6134 14.6667 9.86667V4.13333C14.6667 3.3866 14.6667 3.01323 14.5214 2.72801C14.3936 2.47713 14.1896 2.27316 13.9387 2.14532C13.6535 2 13.2801 2 12.5334 2H12.2667C10.7732 2 10.0265 2 9.45607 2.29065C8.9543 2.54631 8.54635 2.95426 8.29069 3.45603C8.00004 4.02646 8.00004 4.77319 8.00004 6.26667" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 3L4.5 8.5L2 6" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 216 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4L4 12M4 4L12 12" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 219 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.66667 1.34356C8.66667 1.32602 8.66667 1.31725 8.66591 1.30135C8.65018 0.972168 8.3607 0.682824 8.03151 0.667251C8.01562 0.666499 8.0104 0.666501 8.00001 0.666504H5.8391C5.30248 0.666497 4.85957 0.666491 4.49878 0.695968C4.12405 0.726585 3.77958 0.792295 3.45603 0.957155C2.95426 1.21282 2.54631 1.62077 2.29065 2.12253C2.12579 2.44609 2.06008 2.79056 2.02946 3.16529C1.99999 3.52608 1.99999 3.96899 2 4.50562V11.494C1.99999 12.0307 1.99999 12.4736 2.02946 12.8344C2.06008 13.2091 2.12579 13.5536 2.29065 13.8771C2.54631 14.3789 2.95426 14.7869 3.45603 15.0425C3.77958 15.2074 4.12405 15.2731 4.49878 15.3037C4.85958 15.3332 5.30248 15.3332 5.83912 15.3332H10.1609C10.6975 15.3332 11.1404 15.3332 11.5012 15.3037C11.8759 15.2731 12.2204 15.2074 12.544 15.0425C13.0457 14.7869 13.4537 14.3789 13.7093 13.8771C13.8742 13.5536 13.9399 13.2091 13.9705 12.8344C14 12.4736 14 12.0307 14 11.4941V6.66646C14 6.65611 14 6.65093 13.9993 6.63505C13.9837 6.30583 13.6943 6.01631 13.3651 6.0006C13.3492 5.99985 13.3405 5.99985 13.323 5.99985L10.3787 5.99985C10.2105 5.99987 10.0466 5.99989 9.90785 5.98855C9.75545 5.9761 9.57563 5.94672 9.39468 5.85452C9.1438 5.72669 8.93983 5.52272 8.81199 5.27183C8.7198 5.09088 8.69042 4.91106 8.67797 4.75867C8.66663 4.61989 8.66665 4.45603 8.66667 4.28778L8.66667 1.34356ZM5.33333 8.6665C4.96514 8.6665 4.66667 8.96498 4.66667 9.33317C4.66667 9.70136 4.96514 9.99984 5.33333 9.99984H10.6667C11.0349 9.99984 11.3333 9.70136 11.3333 9.33317C11.3333 8.96498 11.0349 8.6665 10.6667 8.6665H5.33333ZM5.33333 11.3332C4.96514 11.3332 4.66667 11.6316 4.66667 11.9998C4.66667 12.368 4.96514 12.6665 5.33333 12.6665H9.33333C9.70152 12.6665 10 12.368 10 11.9998C10 11.6316 9.70152 11.3332 9.33333 11.3332H5.33333Z" fill="#444CE7"/>
<path d="M12.6053 4.6665C12.8011 4.6665 12.8989 4.6665 12.9791 4.61735C13.0923 4.54794 13.16 4.3844 13.129 4.25526C13.107 4.16382 13.0432 4.10006 12.9155 3.97253L10.694 1.75098C10.5664 1.62333 10.5027 1.5595 10.4112 1.53752C10.2821 1.50648 10.1186 1.57417 10.0492 1.6874C10 1.76757 10 1.86545 10 2.0612L10 4.13315C10 4.31982 10 4.41316 10.0363 4.48446C10.0683 4.54718 10.1193 4.59818 10.182 4.63014C10.2533 4.66647 10.3466 4.66647 10.5333 4.66647L12.6053 4.6665Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.66683 4.66667L7.92314 3.17928C7.7091 2.7512 7.60207 2.53715 7.44241 2.38078C7.30122 2.24249 7.13105 2.13732 6.94421 2.07287C6.73294 2 6.49363 2 6.01502 2H3.46683C2.72009 2 2.34672 2 2.06151 2.14532C1.81063 2.27316 1.60665 2.47713 1.47882 2.72801C1.3335 3.01323 1.3335 3.3866 1.3335 4.13333V4.66667M1.3335 4.66667H11.4668C12.5869 4.66667 13.147 4.66667 13.5748 4.88465C13.9511 5.0764 14.2571 5.38236 14.4488 5.75869C14.6668 6.18651 14.6668 6.74656 14.6668 7.86667V10.8C14.6668 11.9201 14.6668 12.4802 14.4488 12.908C14.2571 13.2843 13.9511 13.5903 13.5748 13.782C13.147 14 12.5869 14 11.4668 14H4.5335C3.41339 14 2.85334 14 2.42552 13.782C2.04919 13.5903 1.74323 13.2843 1.55148 12.908C1.3335 12.4802 1.3335 11.9201 1.3335 10.8V4.66667ZM8.00016 11.3333V7.33333M6.00016 9.33333H10.0002" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 985 B

View File

@@ -0,0 +1,23 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3055_14424)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#EC5B27"/>
</g>
<g opacity="0.96">
<path d="M10.2704 24.0002V18.3042H8.87042V20.4962H7.38242V18.3042H5.98242V24.0002H7.38242V21.7442H8.87042V24.0002H10.2704Z" fill="white"/>
<path d="M15.2839 19.5522V18.3042H11.0839V19.5522H12.4839V24.0002H13.8839V19.5522H15.2839Z" fill="white"/>
<path d="M21.4116 24.0002V18.3042H20.0356L18.7556 20.8162L17.4756 18.3042H16.0996V24.0002H17.4996V21.2722L18.3076 22.6802H19.2036L20.0116 21.2722V24.0002H21.4116Z" fill="white"/>
<path d="M26.3525 24.0002V22.7522H23.9605V18.3042H22.5605V24.0002H26.3525Z" fill="white"/>
</g>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3055_14424" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3055_14424"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3055_14424" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,23 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3055_14428)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#2D2D2E"/>
</g>
<g opacity="0.96">
<path d="M9.83907 22.0479V18.3039H8.43907V22.0159C8.43907 22.5599 8.12707 22.7999 7.69507 22.7999C7.38307 22.7999 7.23907 22.6879 7.06307 22.5119L6.14307 23.4239C6.60707 23.8879 7.03107 24.0479 7.69507 24.0479C8.76707 24.0479 9.83907 23.3999 9.83907 22.0479Z" fill="white"/>
<path d="M14.7321 22.2559C14.7321 21.7279 14.6121 21.3039 14.3081 21.0079C14.0681 20.7679 13.7001 20.6079 13.1881 20.5359L12.5001 20.4399C12.3001 20.4159 12.1801 20.3439 12.1081 20.2719C12.0201 20.1839 11.9961 20.0799 11.9961 20.0079C11.9961 19.7599 12.1961 19.4799 12.6841 19.4799C12.9321 19.4799 13.4041 19.4559 13.7641 19.8159L14.6441 18.9359C14.1561 18.4479 13.5401 18.2559 12.7241 18.2559C11.4281 18.2559 10.6441 19.0159 10.6441 20.0559C10.6441 20.5439 10.7721 20.9279 11.0361 21.1999C11.2921 21.4639 11.6761 21.6319 12.1801 21.7039L12.8681 21.7999C13.0521 21.8239 13.1721 21.8799 13.2441 21.9519C13.3241 22.0399 13.3561 22.1519 13.3561 22.2879C13.3561 22.6159 13.0921 22.7999 12.5401 22.7999C12.0841 22.7999 11.5641 22.6959 11.2681 22.3999L10.3721 23.2959C10.9481 23.8879 11.6601 24.0479 12.5321 24.0479C13.7321 24.0479 14.7321 23.4159 14.7321 22.2559Z" fill="white"/>
<path d="M19.8023 21.1519C19.8023 20.2959 19.8263 19.4959 19.2263 18.8959C18.8103 18.4799 18.3303 18.2559 17.6343 18.2559C16.9383 18.2559 16.4583 18.4799 16.0423 18.8959C15.4423 19.4959 15.4663 20.2959 15.4663 21.1519C15.4663 22.0079 15.4423 22.8079 16.0423 23.4079C16.4583 23.8239 16.9383 24.0479 17.6343 24.0479C18.3303 24.0479 18.8103 23.8239 19.2263 23.4079C19.8263 22.8079 19.8023 22.0079 19.8023 21.1519ZM18.4023 21.1519C18.4023 22.1919 18.3223 22.3759 18.1943 22.5439C18.0903 22.6799 17.8903 22.7999 17.6343 22.7999C17.3783 22.7999 17.1783 22.6799 17.0743 22.5439C16.9463 22.3759 16.8663 22.1919 16.8663 21.1519C16.8663 20.1119 16.9463 19.9199 17.0743 19.7519C17.1783 19.6159 17.3783 19.5039 17.6343 19.5039C17.8903 19.5039 18.0903 19.6159 18.1943 19.7519C18.3223 19.9199 18.4023 20.1119 18.4023 21.1519Z" fill="white"/>
<path d="M25.2154 23.9999V18.3039H23.8154V21.1679L21.9914 18.3039H20.7674V23.9999H22.1674V21.1359L23.9914 23.9999H25.2154Z" fill="white"/>
</g>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3055_14428" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3055_14428"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3055_14428" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,18 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3777_37339)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#309BEC"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.9904 25.3335H10.0096C9.45202 25.3335 9 24.9138 9 24.396V18.271C9 17.7532 9.45202 17.3335 10.0096 17.3335H21.9904C22.548 17.3335 23 17.7532 23 18.271V24.396C23 24.9138 22.548 25.3335 21.9904 25.3335ZM12.3654 23.4585V21.021L13.7115 22.5835L15.0577 21.021V23.4585H16.4038V19.2085H15.0577L13.7115 20.771L12.3654 19.2085H11.0192V23.4585H12.3654ZM20.0385 21.3335H21.3846L19.3654 23.521L17.3462 21.3335H18.6923V19.2085H20.0385V21.3335Z" fill="white"/>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3777_37339" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3777_37339"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3777_37339" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,12 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2942_529)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.90599 18.2611L1.75639 15.5832C1.2392 14.9389 0.958496 14.1466 0.958496 13.3312V3.63437C0.958496 2.4129 1.93574 1.39936 3.19644 1.31328L13.1661 0.632614C13.8904 0.583164 14.6103 0.775682 15.2052 1.17794L18.708 3.5462C19.335 3.97012 19.7085 4.66312 19.7085 5.40266V16.427C19.7085 17.6223 18.7476 18.6121 17.5133 18.688L6.44808 19.3692C5.46308 19.4298 4.51099 19.0148 3.90599 18.2611Z" fill="white"/>
<path d="M7.36355 8.48663V8.35968C7.36355 8.03787 7.62129 7.77098 7.95347 7.7488L10.3731 7.58726L13.7191 12.5146V8.19003L12.8579 8.07522V8.01492C12.8579 7.68933 13.1215 7.42068 13.4579 7.40344L15.6595 7.29066V7.60749C15.6595 7.75622 15.5489 7.88343 15.3973 7.90907L14.8675 7.99868V15.0022L14.2026 15.2309C13.6471 15.4219 13.0287 15.2174 12.7107 14.7376L9.46228 9.83568V14.5143L10.4622 14.7056L10.4482 14.7984C10.4046 15.0889 10.1538 15.3086 9.85036 15.3221L7.36355 15.4328C7.33068 15.1204 7.56481 14.8409 7.88781 14.807L8.21492 14.7726V8.53447L7.36355 8.48663Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2553 1.85418L3.28567 2.53485C2.68849 2.57562 2.22559 3.05572 2.22559 3.63431V13.3311C2.22559 13.8748 2.41272 14.4029 2.75752 14.8325L4.90712 17.5104C5.25467 17.9433 5.80161 18.1817 6.36747 18.1469L17.4326 17.4658C17.9998 17.4309 18.4413 16.9761 18.4413 16.4269V5.4026C18.4413 5.06281 18.2697 4.74441 17.9816 4.54963L14.4788 2.18137C14.1218 1.94002 13.6899 1.82451 13.2553 1.85418ZM3.78004 3.78556C3.64138 3.6829 3.70737 3.46903 3.88156 3.45654L13.3224 2.77938C13.6232 2.75781 13.9221 2.84064 14.1653 3.01299L16.0595 4.35502C16.1315 4.40597 16.0977 4.51596 16.0087 4.5208L6.01092 5.06454C5.70835 5.081 5.4097 4.99211 5.16913 4.814L3.78004 3.78556ZM5.54198 6.76913C5.54198 6.44433 5.80438 6.17604 6.13991 6.15777L16.7104 5.5821C17.0374 5.56429 17.3127 5.81577 17.3127 6.13232V15.6782C17.3127 16.0024 17.0512 16.2705 16.7164 16.2895L6.2128 16.8871C5.84887 16.9079 5.54198 16.6282 5.54198 16.2759V6.76913Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2942_529">
<rect width="20" height="20" fill="white" transform="translate(0.333496)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,22 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3055_14420)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#DD3633"/>
</g>
<g opacity="0.96">
<path d="M13.2801 20.1362C13.2801 19.2002 12.6001 18.3042 11.3361 18.3042H9.08008V24.0002H10.4801V21.9682H11.3361C12.6001 21.9682 13.2801 21.0722 13.2801 20.1362ZM11.8801 20.1362C11.8801 20.4322 11.6561 20.7122 11.2721 20.7122H10.4801V19.5602H11.2721C11.6561 19.5602 11.8801 19.8402 11.8801 20.1362Z" fill="white"/>
<path d="M18.3357 21.1522C18.3357 20.2562 18.4077 19.5282 17.7437 18.8642C17.3517 18.4722 16.7997 18.3042 16.2077 18.3042H14.0957V24.0002H16.2077C16.7997 24.0002 17.3517 23.8322 17.7437 23.4402C18.4077 22.7762 18.3357 22.0482 18.3357 21.1522ZM16.9357 21.1522C16.9357 22.1202 16.8957 22.2722 16.7837 22.4322C16.6557 22.6242 16.4637 22.7522 16.1117 22.7522H15.4957V19.5522H16.1117C16.4637 19.5522 16.6557 19.6802 16.7837 19.8722C16.8957 20.0322 16.9357 20.1922 16.9357 21.1522Z" fill="white"/>
<path d="M23.1786 19.5522V18.3042H19.3066V24.0002H20.7066V21.8002H22.8186V20.5522H20.7066V19.5522H23.1786Z" fill="white"/>
</g>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3055_14420" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3055_14420"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3055_14420" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.66675 6.00004C8.95541 6.00004 10.0001 4.95537 10.0001 3.66671C10.0001 2.37804 8.95541 1.33337 7.66675 1.33337C6.37808 1.33337 5.33341 2.37804 5.33341 3.66671C5.33341 4.95537 6.37808 6.00004 7.66675 6.00004Z" fill="#444CE7"/>
<path d="M4.65042 5.75205C4.49131 5.52233 4.41175 5.40747 4.32605 5.36953C4.24913 5.33547 4.16814 5.3313 4.08813 5.35729C3.99899 5.38624 3.92889 5.46803 3.78869 5.6316C3.22609 6.28801 2.84238 7.10277 2.71398 8.00016H2.66675C2.29856 8.00016 2.00008 7.70168 2.00008 7.33349C2.00008 7.08757 2.13289 6.87188 2.3339 6.75561C2.65261 6.57125 2.76151 6.16343 2.57715 5.84472C2.39279 5.52601 1.98497 5.4171 1.66626 5.60147C1.0702 5.94627 0.666748 6.59237 0.666748 7.33349C0.666748 8.43806 1.56218 9.33349 2.66675 9.33349H2.71402C2.85798 10.339 3.32264 11.2416 4.00004 11.9329L4.00004 13.4823C4.00001 13.5617 3.99997 13.6565 4.0068 13.7401C4.01471 13.8369 4.035 13.9756 4.10903 14.1209C4.20491 14.309 4.35789 14.462 4.54605 14.5579C4.69135 14.6319 4.83001 14.6522 4.92682 14.6601C5.01034 14.6669 5.10517 14.6669 5.18456 14.6669H6.48214C6.56153 14.6669 6.6564 14.6669 6.73993 14.6601C6.83674 14.6522 6.9754 14.6319 7.1207 14.5579C7.30886 14.462 7.46184 14.309 7.55771 14.1209C7.63175 13.9756 7.65204 13.8369 7.65995 13.7401C7.66678 13.6565 7.66674 13.5617 7.66671 13.4823L7.66671 13.3335H8.33338L8.33338 13.482C8.33335 13.5615 8.33331 13.6563 8.34014 13.7398C8.34805 13.8366 8.36834 13.9753 8.44238 14.1206C8.53825 14.3088 8.69123 14.4618 8.87939 14.5576C9.02469 14.6317 9.16335 14.652 9.26016 14.6599C9.34368 14.6667 9.43849 14.6667 9.51788 14.6666H10.8155C10.8949 14.6667 10.9898 14.6667 11.0733 14.6599C11.1701 14.652 11.3088 14.6317 11.4541 14.5576C11.6422 14.4618 11.7952 14.3088 11.8911 14.1206C11.9651 13.9753 11.9854 13.8366 11.9933 13.7398C12.0002 13.6563 12.0001 13.5615 12.0001 13.482L12.0001 12.884C12.7376 12.5338 13.3679 11.9963 13.8302 11.3333L14.1488 11.3333C14.2283 11.3333 14.3231 11.3334 14.4066 11.3265C14.5035 11.3186 14.6421 11.2983 14.7874 11.2243C14.9756 11.1284 15.1286 10.9754 15.2244 10.7873C15.2985 10.642 15.3188 10.5033 15.3267 10.4065C15.3335 10.323 15.3335 10.2282 15.3334 10.1488V7.84245C15.3335 7.76683 15.3335 7.67651 15.3273 7.59685C15.3201 7.50472 15.3017 7.37257 15.2344 7.23269C15.1361 7.02862 14.9714 6.86396 14.7674 6.76567C14.6275 6.6983 14.4953 6.67993 14.4032 6.67276C14.3437 6.66813 14.2783 6.66697 14.2175 6.66669C13.9938 6.19554 13.6937 5.76825 13.3334 5.40063L13.3334 4.68824C13.3335 4.58502 13.3335 4.46819 13.3245 4.36769C13.3147 4.25673 13.2888 4.08518 13.1835 3.91515C13.0521 3.70315 12.8463 3.54789 12.6064 3.47982C12.4139 3.42523 12.2419 3.44753 12.1325 3.4685C12.0334 3.48749 11.921 3.51963 11.8218 3.54801L11.7179 3.57769C11.5813 3.61674 11.513 3.63626 11.4649 3.67145C11.4185 3.70542 11.3901 3.73891 11.3642 3.79026C11.3374 3.84344 11.3281 3.92456 11.3096 4.08678C11.1012 5.91399 9.54973 7.33337 7.66675 7.33337C6.41642 7.33337 5.31225 6.70755 4.65042 5.75205Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.33325 12.6666C1.33325 12.2984 1.63173 12 1.99992 12H4.66659C5.03478 12 5.33325 12.2984 5.33325 12.6666C5.33325 13.0348 5.03478 13.3333 4.66659 13.3333H1.99992C1.63173 13.3333 1.33325 13.0348 1.33325 12.6666Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5528 12C10.2782 11.2232 9.5374 10.6666 8.66659 10.6666C7.56202 10.6666 6.66659 11.5621 6.66659 12.6666C6.66659 13.7712 7.56202 14.6666 8.66659 14.6666C9.5374 14.6666 10.2782 14.1101 10.5528 13.3333L13.9999 13.3333C14.3681 13.3333 14.6666 13.0348 14.6666 12.6666C14.6666 12.2984 14.3681 12 13.9999 12L10.5528 12Z" fill="#155EEF"/>
<path d="M9.99992 7.33329C9.63173 7.33329 9.33325 7.63177 9.33325 7.99996C9.33325 8.36815 9.63173 8.66663 9.99992 8.66663H13.9999C14.3681 8.66663 14.6666 8.36815 14.6666 7.99996C14.6666 7.63177 14.3681 7.33329 13.9999 7.33329H9.99992Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.33325 7.99996C1.33325 7.63177 1.63173 7.33329 1.99992 7.33329H4.11372C4.38828 6.5565 5.12911 5.99996 5.99992 5.99996C7.10449 5.99996 7.99992 6.89539 7.99992 7.99996C7.99992 9.10453 7.10449 9.99996 5.99992 9.99996C5.12911 9.99996 4.38828 9.44342 4.11372 8.66663H1.99992C1.63173 8.66663 1.33325 8.36815 1.33325 7.99996Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.99992 1.33329C10.8707 1.33329 11.6116 1.88983 11.8861 2.66663H13.9999C14.3681 2.66663 14.6666 2.9651 14.6666 3.33329C14.6666 3.70148 14.3681 3.99996 13.9999 3.99996H11.8861C11.6116 4.77675 10.8707 5.33329 9.99992 5.33329C8.89535 5.33329 7.99992 4.43786 7.99992 3.33329C7.99992 2.22872 8.89535 1.33329 9.99992 1.33329Z" fill="#155EEF"/>
<path d="M1.33325 3.33329C1.33325 2.9651 1.63173 2.66663 1.99992 2.66663H5.99992C6.36811 2.66663 6.66659 2.9651 6.66659 3.33329C6.66659 3.70148 6.36811 3.99996 5.99992 3.99996H1.99992C1.63173 3.99996 1.33325 3.70148 1.33325 3.33329Z" fill="#155EEF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00008 0.666626C8.36827 0.666626 8.66675 0.965103 8.66675 1.33329V2.66663C8.66675 3.03482 8.36827 3.33329 8.00008 3.33329C7.63189 3.33329 7.33341 3.03482 7.33341 2.66663V1.33329C7.33341 0.965103 7.63189 0.666626 8.00008 0.666626Z" fill="#FB6514"/>
<path d="M3.75729 2.81452C3.49694 2.55418 3.07483 2.55418 2.81448 2.81452C2.55413 3.07487 2.55413 3.49698 2.81448 3.75733L3.75729 4.70014C4.01764 4.96049 4.43975 4.96049 4.7001 4.70014C4.96045 4.43979 4.96045 4.01768 4.7001 3.75733L3.75729 2.81452Z" fill="#FB6514"/>
<path d="M0.666748 7.99996C0.666748 7.63177 0.965225 7.33329 1.33341 7.33329H2.66675C3.03494 7.33329 3.33341 7.63177 3.33341 7.99996C3.33341 8.36815 3.03494 8.66663 2.66675 8.66663H1.33341C0.965225 8.66663 0.666748 8.36815 0.666748 7.99996Z" fill="#FB6514"/>
<path d="M13.3334 7.33329C12.9652 7.33329 12.6667 7.63177 12.6667 7.99996C12.6667 8.36815 12.9652 8.66663 13.3334 8.66663H14.6667C15.0349 8.66663 15.3334 8.36815 15.3334 7.99996C15.3334 7.63177 15.0349 7.33329 14.6667 7.33329H13.3334Z" fill="#FB6514"/>
<path d="M12.2426 11.2997C11.9823 11.0394 11.5602 11.0394 11.2998 11.2997C11.0395 11.5601 11.0395 11.9822 11.2998 12.2425L12.2426 13.1853C12.503 13.4457 12.9251 13.4457 13.1855 13.1853C13.4458 12.925 13.4458 12.5029 13.1855 12.2425L12.2426 11.2997Z" fill="#FB6514"/>
<path d="M13.1855 3.75733C13.4458 3.49698 13.4458 3.07487 13.1855 2.81452C12.9251 2.55418 12.503 2.55418 12.2426 2.81452L11.2998 3.75733C11.0395 4.01768 11.0395 4.43979 11.2998 4.70014C11.5602 4.96049 11.9823 4.96049 12.2426 4.70014L13.1855 3.75733Z" fill="#FB6514"/>
<path d="M8.00008 12.6666C8.36827 12.6666 8.66675 12.9651 8.66675 13.3333V14.6666C8.66675 15.0348 8.36827 15.3333 8.00008 15.3333C7.63189 15.3333 7.33341 15.0348 7.33341 14.6666V13.3333C7.33341 12.9651 7.63189 12.6666 8.00008 12.6666Z" fill="#FB6514"/>
<path d="M4.7001 12.2425C4.96045 11.9822 4.96045 11.5601 4.7001 11.2997C4.43975 11.0394 4.01764 11.0394 3.75729 11.2997L2.81448 12.2425C2.55413 12.5029 2.55413 12.925 2.81448 13.1853C3.07483 13.4457 3.49694 13.4457 3.75729 13.1853L4.7001 12.2425Z" fill="#FB6514"/>
<path d="M8.59791 4.37154C8.48559 4.14401 8.25385 3.99996 8.0001 3.99996C7.74635 3.99996 7.51461 4.14401 7.4023 4.37154L6.52726 6.14427L4.57035 6.4303C4.31931 6.46699 4.11085 6.643 4.0326 6.88434C3.95435 7.12567 4.01987 7.39051 4.20161 7.56753L5.61709 8.9462L5.28303 10.8939C5.24013 11.1441 5.34296 11.3968 5.54828 11.546C5.7536 11.6951 6.02579 11.7148 6.2504 11.5967L8.0001 10.6765L9.7498 11.5967C9.97441 11.7148 10.2466 11.6951 10.4519 11.546C10.6572 11.3968 10.7601 11.1441 10.7172 10.8939L10.3831 8.9462L11.7986 7.56753C11.9803 7.39051 12.0458 7.12567 11.9676 6.88434C11.8893 6.643 11.6809 6.46699 11.4299 6.4303L9.47294 6.14427L8.59791 4.37154Z" fill="#FB6514"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@@ -0,0 +1,23 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3055_14432)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#E3E5E8"/>
<path d="M4.25 7.73349C4.25 6.60926 4.25019 5.78113 4.30367 5.12666C4.3569 4.47511 4.46169 4.01774 4.65873 3.63103C5.01825 2.92542 5.59193 2.35175 6.29754 1.99222C6.68424 1.79518 7.14162 1.6904 7.79317 1.63716C8.44763 1.58369 9.27577 1.5835 10.4 1.5835H18.5631L27.75 10.7704V24.2668C27.75 25.3911 27.7498 26.2192 27.6963 26.8737C27.6431 27.5252 27.5383 27.9826 27.3413 28.3693C26.9817 29.0749 26.4081 29.6486 25.7025 30.0081C25.3158 30.2051 24.8584 30.3099 24.2068 30.3632C23.5524 30.4166 22.7242 30.4168 21.6 30.4168H10.4C9.27577 30.4168 8.44763 30.4166 7.79317 30.3632C7.14162 30.3099 6.68424 30.2051 6.29754 30.0081C5.59193 29.6486 5.01825 29.0749 4.65873 28.3693C4.46169 27.9826 4.3569 27.5252 4.30367 26.8737C4.25019 26.2192 4.25 25.3911 4.25 24.2668V7.73349Z" stroke="black" stroke-opacity="0.03" stroke-width="0.5"/>
</g>
<g opacity="0.96">
<path d="M13.2254 19.5522V18.3042H9.02539V19.5522H10.4254V24.0002H11.8254V19.5522H13.2254Z" fill="#667085"/>
<path d="M18.5371 24.0002L16.7611 21.0802L18.4251 18.3042H16.8331L16.0011 19.9122L15.1691 18.3042H13.5771L15.2411 21.0802L13.4651 24.0002H15.0651L16.0011 22.2482L16.9371 24.0002H18.5371Z" fill="#667085"/>
<path d="M22.9754 19.5522V18.3042H18.7754V19.5522H20.1754V24.0002H21.5754V19.5522H22.9754Z" fill="#667085"/>
</g>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3055_14432" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3055_14432"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3055_14432" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,23 @@
<svg width="32" height="34" viewBox="0 0 32 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3055_14436)">
<path d="M4 7.73349C4 5.49329 4 4.37318 4.43597 3.51753C4.81947 2.76489 5.43139 2.15296 6.18404 1.76947C7.03969 1.3335 8.15979 1.3335 10.4 1.3335H18.6667L28 10.6668V24.2668C28 26.507 28 27.6271 27.564 28.4828C27.1805 29.2354 26.5686 29.8474 25.816 30.2309C24.9603 30.6668 23.8402 30.6668 21.6 30.6668H10.4C8.15979 30.6668 7.03969 30.6668 6.18404 30.2309C5.43139 29.8474 4.81947 29.2354 4.43597 28.4828C4 27.6271 4 26.507 4 24.2668V7.73349Z" fill="#E3E5E8"/>
<path d="M4.25 7.73349C4.25 6.60926 4.25019 5.78113 4.30367 5.12666C4.3569 4.47511 4.46169 4.01774 4.65873 3.63103C5.01825 2.92542 5.59193 2.35175 6.29754 1.99222C6.68424 1.79518 7.14162 1.6904 7.79317 1.63716C8.44763 1.58369 9.27577 1.5835 10.4 1.5835H18.5631L27.75 10.7704V24.2668C27.75 25.3911 27.7498 26.2192 27.6963 26.8737C27.6431 27.5252 27.5383 27.9826 27.3413 28.3693C26.9817 29.0749 26.4081 29.6486 25.7025 30.0081C25.3158 30.2051 24.8584 30.3099 24.2068 30.3632C23.5524 30.4166 22.7242 30.4168 21.6 30.4168H10.4C9.27577 30.4168 8.44763 30.4166 7.79317 30.3632C7.14162 30.3099 6.68424 30.2051 6.29754 30.0081C5.59193 29.6486 5.01825 29.0749 4.65873 28.3693C4.46169 27.9826 4.3569 27.5252 4.30367 26.8737C4.25019 26.2192 4.25 25.3911 4.25 24.2668V7.73349Z" stroke="black" stroke-opacity="0.03" stroke-width="0.5"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9998 23.1992C15.8014 23.1992 15.6039 23.1968 15.4077 23.1924V24.0549C15.4077 24.3819 15.6728 24.647 15.9998 24.647C16.3268 24.647 16.592 24.3819 16.592 24.0549V23.1924C16.3957 23.1968 16.1983 23.1992 15.9998 23.1992Z" fill="#98A2B3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0984 22.8838L11.757 23.8593C11.649 24.168 11.8117 24.5058 12.1203 24.6138C12.185 24.6364 12.251 24.6472 12.3159 24.6472C12.5605 24.6472 12.7894 24.4944 12.8747 24.2505L13.2936 23.0534C12.8807 23.0073 12.481 22.9506 12.0984 22.8838Z" fill="#98A2B3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.2431 23.8593L19.9018 22.8838C19.5192 22.9506 19.1195 23.0073 18.7065 23.0534L19.1254 24.2505C19.2108 24.4944 19.4396 24.6472 19.6843 24.6472C19.7491 24.6472 19.8151 24.6364 19.8798 24.6138C20.1885 24.5058 20.3511 24.168 20.2431 23.8593Z" fill="#98A2B3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.1624 17.2634C20.2697 17.6416 20.3254 18.0369 20.3254 18.4409C20.3254 18.9087 20.05 19.3327 19.6226 19.5228C19.5564 19.5522 17.9801 20.2436 16.0359 20.2436C14.0917 20.2436 12.5153 19.5522 12.4492 19.5228C12.0218 19.3327 11.7464 18.9086 11.7464 18.4409C11.7464 18.0312 11.8037 17.6305 11.914 17.2476C10.3343 17.5645 8.5 18.2009 8.5 19.4464C8.5 20.2859 9.32512 20.9477 10.9525 21.4134C11.4194 21.547 11.9381 21.66 12.4949 21.7506C12.8783 21.813 13.28 21.8648 13.6953 21.9056C14.2455 21.9597 14.8197 21.9942 15.4079 22.0082C15.6039 22.0128 15.8013 22.0153 16 22.0153C16.1987 22.0153 16.3962 22.0128 16.5921 22.0082C17.1803 21.9943 17.7545 21.9596 18.3047 21.9056C18.72 21.8648 19.1217 21.8131 19.5051 21.7506C20.062 21.66 20.5807 21.547 21.0476 21.4134C22.6749 20.9477 23.5 20.2859 23.5 19.4464C23.5 18.2187 21.7108 17.5833 20.1624 17.2634Z" fill="#98A2B3"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8441 17.1144C18.7585 16.9335 18.6559 16.7622 18.5384 16.6025C18.4174 16.4382 18.2809 16.286 18.1307 16.1486C17.5784 15.6437 16.8433 15.3354 16.036 15.3354C15.2318 15.3354 14.499 15.6411 13.9476 16.1426C13.7974 16.2791 13.6609 16.4303 13.5399 16.5937C13.4217 16.753 13.3185 16.924 13.2322 17.1048C13.039 17.5095 12.9307 17.9624 12.9307 18.4407C12.9307 18.4407 14.321 19.0592 16.036 19.0592C17.751 19.0592 19.1412 18.4407 19.1412 18.4407C19.1412 17.9662 19.0344 17.5167 18.8441 17.1144Z" fill="#98A2B3"/>
<path opacity="0.5" d="M18.6665 1.3335L27.9998 10.6668H21.3332C19.8604 10.6668 18.6665 9.47292 18.6665 8.00016V1.3335Z" fill="white"/>
<defs>
<filter id="filter0_d_3055_14436" x="2" y="0.333496" width="28" height="33.3335" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3055_14436"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3055_14436" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M4 16.2422C2.79401 15.435 2 14.0602 2 12.5C2 10.1564 3.79151 8.23129 6.07974 8.01937C6.54781 5.17213 9.02024 3 12 3C14.9798 3 17.4522 5.17213 17.9203 8.01937C20.2085 8.23129 22 10.1564 22 12.5C22 14.0602 21.206 15.435 20 16.2422" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 16L12 12M12 12L16 16M12 12L12 21" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 572 B

View File

@@ -0,0 +1,4 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.06124 2.21694C7.57213 2.07545 8.11041 1.99984 8.66634 1.99984C9.97432 1.99984 11.1845 2.41837 12.1704 3.12881C11.6459 3.4755 11.2546 3.85196 10.9834 4.25446C10.569 4.86947 10.4628 5.50546 10.5486 6.08575C10.632 6.64928 10.8907 7.13053 11.1627 7.48656C11.4265 7.83177 11.7501 8.12048 12.0351 8.26293C12.7526 8.62164 13.5806 8.80129 14.1996 8.89387C14.7596 8.97762 15.9468 9.2579 15.9907 8.36596C15.9967 8.24461 15.9997 8.12255 15.9997 7.99984C15.9997 3.94975 12.7164 0.666504 8.66634 0.666504C4.61625 0.666504 1.33301 3.94975 1.33301 7.99984C1.33301 12.0499 4.61625 15.3332 8.66634 15.3332C8.78906 15.3332 8.91112 15.3301 9.03246 15.3242C9.40021 15.3061 9.68364 14.9933 9.66553 14.6255C9.64743 14.2578 9.33463 13.9743 8.96689 13.9925C8.86737 13.9974 8.76717 13.9998 8.66634 13.9998C6.32676 13.9998 4.29993 12.6608 3.31046 10.7073L3.95617 10.3345L6.33797 10.7803C6.95507 10.8958 7.52471 10.4207 7.52192 9.79289L7.51258 7.69081L8.72983 5.60684C8.92705 5.2692 8.90938 4.84758 8.68459 4.52762L7.06124 2.21694Z" fill="#1570EF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.96344 8.43724C9.71815 8.3442 9.4411 8.40367 9.2556 8.58917C9.07009 8.77467 9.01063 9.05172 9.10367 9.29701L11.2149 14.8629C11.3123 15.1198 11.5575 15.2906 11.8323 15.2931C12.1071 15.2955 12.3552 15.1291 12.4572 14.8739L13.3377 12.6713L15.5403 11.7908C15.7955 11.6888 15.9619 11.4407 15.9595 11.1659C15.9571 10.891 15.7862 10.6459 15.5293 10.5484L9.96344 8.43724Z" fill="#1570EF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.66675 11.6666C1.66675 11.2985 1.96522 11 2.33341 11H6.00008C6.36827 11 6.66675 11.2985 6.66675 11.6666C6.66675 12.0348 6.36827 12.3333 6.00008 12.3333H2.33341C1.96522 12.3333 1.66675 12.0348 1.66675 11.6666Z" fill="#7839EE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666748 7.99997C0.666748 7.63178 0.965225 7.33331 1.33341 7.33331H4.33341C4.7016 7.33331 5.00008 7.63178 5.00008 7.99997C5.00008 8.36816 4.7016 8.66664 4.33341 8.66664H1.33341C0.965225 8.66664 0.666748 8.36816 0.666748 7.99997Z" fill="#7839EE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.00008 4.33331C2.00008 3.96512 2.29856 3.66664 2.66675 3.66664H6.00008C6.36827 3.66664 6.66675 3.96512 6.66675 4.33331C6.66675 4.7015 6.36827 4.99997 6.00008 4.99997H2.66675C2.29856 4.99997 2.00008 4.7015 2.00008 4.33331Z" fill="#7839EE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5785 1.37998C11.8632 1.49253 12.0347 1.78458 11.9942 2.08808L11.4282 6.33331H13.9637C13.9714 6.33331 13.979 6.33331 13.9867 6.3333C14.1339 6.33327 14.2864 6.33324 14.4124 6.34476C14.5363 6.35609 14.7618 6.38612 14.9633 6.54167C15.1984 6.72308 15.3407 6.99955 15.3517 7.29625C15.3611 7.55067 15.2545 7.7516 15.1917 7.85904C15.1278 7.96825 15.0391 8.09234 14.9536 8.21207C14.9491 8.21833 14.9446 8.22457 14.9402 8.23079L10.5426 14.3875C10.3646 14.6366 10.0398 14.7325 9.75503 14.62C9.47027 14.5074 9.29879 14.2154 9.33926 13.9119L9.90529 9.66664H7.36978C7.36213 9.66664 7.35447 9.66664 7.34679 9.66664C7.19962 9.66668 7.0471 9.66671 6.92111 9.65519C6.79717 9.64386 6.5717 9.61383 6.37015 9.45828C6.13511 9.27687 5.99283 9.0004 5.98183 8.7037C5.9724 8.44928 6.07901 8.24835 6.14183 8.14091C6.20569 8.0317 6.29437 7.9076 6.37993 7.78787C6.3844 7.78162 6.38886 7.77538 6.3933 7.76915L10.7909 1.61248C10.9689 1.36332 11.2937 1.26743 11.5785 1.37998Z" fill="#7839EE"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,38 @@
.modal {
position: relative;
}
.modalHeader {
@apply flex items-center place-content-between h-8;
}
.modalHeader .title {
@apply grow;
font-weight: 600;
font-size: 20px;
line-height: 32px;
color: #101828;
}
.modalHeader .close {
@apply shrink-0 h-4 w-4 bg-center bg-no-repeat cursor-pointer;
background-image: url(../assets/close.svg);
background-size: 16px;
}
.modal .tip {
@apply mt-1 mb-8;
font-weight: 400;
font-size: 13px;
line-height: 18px;
color: #667085;
}
.form {
@apply mb-8;
}
.form .label {
@apply mb-2;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #101828;
}

View File

@@ -0,0 +1,73 @@
'use client'
import React, { useState } from 'react'
import { useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import Modal from '@/app/components/base/modal'
import Input from '@/app/components/base/input'
import Button from '@/app/components/base/button'
import { ToastContext } from '@/app/components/base/toast'
import { createEmptyDataset } from '@/service/datasets'
import cn from 'classnames'
import s from './index.module.css'
type IProps = {
show: boolean,
onHide: () => void,
}
const EmptyDatasetCreationModal = ({
show = false,
onHide,
}: IProps) => {
const [inputValue, setInputValue] = useState('')
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const router = useRouter()
const submit = async () => {
if (!inputValue) {
notify({ type: 'error', message: t('datasetCreation.stepOne.modal.nameNotEmpty') })
return
}
if (inputValue.length > 40) {
notify({ type: 'error', message: t('datasetCreation.stepOne.modal.nameLengthInvaild') })
return
}
try {
const dataset = await createEmptyDataset({ name: inputValue })
onHide()
router.push(`/datasets/${dataset.id}/documents`)
}
catch (err) {
notify({ type: 'error', message: t('datasetCreation.stepOne.modal.failed') })
return
}
}
return (
<Modal
isShow={show}
onClose={onHide}
className={cn(s.modal, '!max-w-[520px]', 'px-8')}
>
<div className={s.modalHeader}>
<div className={s.title}>{t('datasetCreation.stepOne.modal.title')}</div>
<span className={s.close} onClick={onHide}/>
</div>
<div className={s.tip}>{t('datasetCreation.stepOne.modal.tip')}</div>
<div className={s.form}>
<div className={s.label}>{t('datasetCreation.stepOne.modal.input')}</div>
<Input className='!h-8' value={inputValue} placeholder={t('datasetCreation.stepOne.modal.placeholder') || ''} onChange={setInputValue} />
</div>
<div className='flex flex-row-reverse'>
<Button className='w-24 ml-2' type='primary' onClick={submit}>{t('datasetCreation.stepOne.modal.confirmButton')}</Button>
<Button className='w-24' onClick={onHide}>{t('datasetCreation.stepOne.modal.cancelButton')}</Button>
</div>
</Modal>
)
}
export default EmptyDatasetCreationModal

View File

@@ -0,0 +1,46 @@
.filePreview {
@apply flex flex-col border-l border-gray-200 shrink-0;
width: 528px;
background-color: #fcfcfd;
}
.previewHeader {
@apply border-b border-gray-200 shrink-0;
margin: 42px 32px 0;
padding-bottom: 16px;
}
.previewHeader .title {
color: #101828;
font-weight: 600;
font-size: 18px;
line-height: 28px;
}
.previewHeader .fileName {
font-weight: 400;
font-size: 12px;
line-height: 18px;
color: #1D2939;
}
.previewHeader .filetype {
color: #667085;
}
.previewContent {
@apply overflow-y-auto grow;
padding: 20px 32px;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.previewContent .loading {
width: 100%;
height: 180px;
background: #f9fafb center no-repeat url(../assets/Loading.svg);
background-size: contain;
}

View File

@@ -0,0 +1,62 @@
'use client'
import React, { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import type { File } from '@/models/datasets'
import { fetchFilePreview } from '@/service/common'
import cn from 'classnames'
import s from './index.module.css'
type IProps = {
file?: File,
}
const FilePreview = ({
file,
}: IProps) => {
const { t } = useTranslation()
const [previewContent, setPreviewContent] = useState('')
const [loading, setLoading] = useState(true)
const getPreviewContent = async (fileID: string) => {
try {
const res = await fetchFilePreview({ fileID })
setPreviewContent(res.content)
setLoading(false)
}
catch {}
}
const getFileName = (currentFile?: File) => {
if (!currentFile) {
return ''
}
const arr = currentFile.name.split('.')
return arr.slice(0, -1).join()
}
useEffect(() => {
if (file) {
getPreviewContent(file.id)
}
}, [file])
return (
<div className={cn(s.filePreview)}>
<div className={cn(s.previewHeader)}>
<div className={cn(s.title)}>{t('datasetCreation.stepOne.filePreview')}</div>
<div className={cn(s.fileName)}>
<span>{getFileName(file)}</span><span className={cn(s.filetype)}>.{file?.extension}</span>
</div>
</div>
<div className={cn(s.previewContent)}>
{loading && <div className={cn(s.loading)}/>}
{!loading && (
<div className={cn(s.fileContent)}>{previewContent}</div>
)}
</div>
</div>
)
}
export default FilePreview

View File

@@ -0,0 +1,171 @@
.fileUploader {
@apply mb-9;
}
.fileUploader .title {
@apply mb-2;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.fileUploader .tip {
@apply mt-2;
font-weight: 400;
font-size: 12px;
line-height: 26px;
color: #667085;
}
.uploader {
@apply relative box-border flex justify-center items-center;
max-width: 640px;
height: 80px;
background: #F9FAFB;
border: 1px dashed #EAECF0;
border-radius: 12px;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #667085;
}
.uploader.dragging {
background: #F5F8FF;
border: 1px dashed #B2CCFF;
}
.uploader .draggingCover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.uploader::before {
content: '';
display: block;
margin-right: 8px;
width: 24px;
height: 24px;
background: center no-repeat url(../assets/upload-cloud-01.svg);
background-size: contain;
}
.uploader .browse{
@apply pl-1 cursor-pointer;
color: #155eef;
}
.file {
@apply box-border relative flex items-center;
padding: 21px 24px 21px 64px;
max-width: 640px;
height: 80px;
background: #F9FAFB;
border: 1px solid #F2F4F7;
border-radius: 12px;
overflow: hidden;
}
.progressbar {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: #F2F4F7;
}
.file:hover {
background: #F5F8FF;
border: 1px solid #D1E0FF;
}
.file:hover .actionWrapper .buttonWrapper {
display: flex;
align-items: center;
}
.file:hover .actionWrapper .divider {
display: block;
}
.file.uploading,
.file.uploading:hover {
background: #FCFCFD;
border: 1px solid #EAECF0;
}
.file.uploading:hover .actionWrapper .percent {
padding: 8px;
}
.file.uploading:hover .actionWrapper .buttonWrapper {
display: flex;
align-items: center;
}
.fileIcon {
@apply w-8 h-8 bg-center bg-no-repeat;
position: absolute;
top: 24px;
left: 24px;
background-image: url(../assets/unknow.svg);
background-size: 32px;
}
.fileIcon.pdf {
background-image: url(../assets/pdf.svg);
}
.fileIcon.html,
.fileIcon.htm {
background-image: url(../assets/html.svg);
}
.fileIcon.md,
.fileIcon.markdown {
background-image: url(../assets/md.svg);
}
.fileIcon.txt {
background-image: url(../assets/txt.svg);
}
.fileIcon.json {
background-image: url(../assets/json.svg);
}
.fileInfo {
@apply grow;
z-index: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.filename {
font-weight: 500;
font-size: 14px;
line-height: 20px;
}
.name {
color: #1D2939;
line-height: 20px;
}
.extension {
color: #667085;
line-height: 20px;
}
.fileExtraInfo {
color: #667085;
font-size: 12px;
line-height: 18px;
}
.actionWrapper {
@apply flex items-center shrink-0;
z-index: 1;
}
.actionWrapper .percent {
font-size: 16px;
line-height: 24px;
color: #344054;
}
.actionWrapper .divider {
display: none;
margin: 0 8px;
width: 1px;
height: 16px;
background: #FEE4E2;
}
.actionWrapper .remove {
width: 32px;
height: 32px;
background: center no-repeat url(../assets/trash.svg);
background-size: 16px;
cursor: pointer;
}
.actionWrapper .buttonWrapper {
@apply flex items-center;
display: none;
}

View File

@@ -0,0 +1,265 @@
'use client'
import React, { useState, useRef, useEffect, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import type { File as FileEntity } from '@/models/datasets'
import { useContext } from 'use-context-selector'
import { ToastContext } from '@/app/components/base/toast'
import Button from '@/app/components/base/button'
import { upload } from '@/service/base'
import cn from 'classnames'
import s from './index.module.css'
type IFileUploaderProps = {
file?: FileEntity;
onFileUpdate: (file?: FileEntity) => void;
}
const ACCEPTS = [
'.pdf',
'.html',
'.htm',
'.md',
'.markdown',
'.txt',
]
const MAX_SIZE = 15 * 1024 *1024
const FileUploader = ({ file, onFileUpdate }: IFileUploaderProps) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const [dragging, setDragging] = useState(false)
const dropRef = useRef<HTMLDivElement>(null)
const dragRef = useRef<HTMLDivElement>(null)
const fileUploader = useRef<HTMLInputElement>(null)
const uploadPromise = useRef<any>(null)
const [currentFile, setCurrentFile] = useState<File>()
const [uploading, setUploading] = useState(false)
const [percent, setPercent] = useState(0)
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
e.target !== dragRef.current && setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
}
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
e.target === dragRef.current && setDragging(false)
}
const handleDrop = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
setDragging(false)
if (!e.dataTransfer) {
return
}
const files = [...e.dataTransfer.files]
if (files.length > 1) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.count') })
return;
}
onFileUpdate()
fileUpload(files[0])
}
const selectHandle = () => {
if (fileUploader.current) {
fileUploader.current.click();
}
}
const removeFile = () => {
if (fileUploader.current) {
fileUploader.current.value = ''
}
setCurrentFile(undefined)
onFileUpdate()
}
const fileChangeHandle = (e: React.ChangeEvent<HTMLInputElement>) => {
const currentFile = e.target.files?.[0]
onFileUpdate()
fileUpload(currentFile)
}
const fileUpload = async (file?: File) => {
if (!file) {
return
}
if (!isValid(file)) {
return
}
setCurrentFile(file)
setUploading(true)
const formData = new FormData()
formData.append('file', file)
// store for abort
const currentXHR = new XMLHttpRequest()
uploadPromise.current = currentXHR
try {
const result = await upload({
xhr: currentXHR,
data: formData,
onprogress: onProgress,
}) as FileEntity;
onFileUpdate(result)
setUploading(false)
}
catch (xhr: any) {
setUploading(false)
// abort handle
if (xhr.readyState === 0 && xhr.status === 0) {
if (fileUploader.current) {
fileUploader.current.value = ''
}
setCurrentFile(undefined)
return
}
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.failed') })
return
}
}
const onProgress = useCallback((e: ProgressEvent) => {
if (e.lengthComputable) {
const percent = Math.floor(e.loaded / e.total * 100)
setPercent(percent)
}
}, [setPercent])
const abort = () => {
const currentXHR = uploadPromise.current
currentXHR.abort();
}
// utils
const getFileType = (currentFile: File) => {
if (!currentFile) {
return ''
}
const arr = currentFile.name.split('.')
return arr[arr.length-1]
}
const getFileName = (name: string) => {
const arr = name.split('.')
return arr.slice(0, -1).join()
}
const getFileSize = (size: number) => {
if (size / 1024 < 10) {
return `${(size / 1024).toFixed(2)}KB`
}
return `${(size / 1024 / 1024).toFixed(2)}MB`
}
const isValid = (file: File) => {
const { size } = file
const ext = `.${getFileType(file)}`
const isValidType = ACCEPTS.includes(ext)
if (!isValidType) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.typeError') })
}
const isValidSize = size <= MAX_SIZE;
if (!isValidSize) {
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.size') })
}
return isValidType && isValidSize;
}
useEffect(() => {
dropRef.current?.addEventListener('dragenter', handleDragEnter);
dropRef.current?.addEventListener('dragover', handleDragOver);
dropRef.current?.addEventListener('dragleave', handleDragLeave);
dropRef.current?.addEventListener('drop', handleDrop);
return () => {
dropRef.current?.removeEventListener('dragenter', handleDragEnter);
dropRef.current?.removeEventListener('dragover', handleDragOver);
dropRef.current?.removeEventListener('dragleave', handleDragLeave);
dropRef.current?.removeEventListener('drop', handleDrop);
}
}, [])
return (
<div className={s.fileUploader}>
<input
ref={fileUploader}
style={{ display: 'none' }}
type="file"
id="fileUploader"
accept={ACCEPTS.join(',')}
onChange={fileChangeHandle}
/>
<div className={s.title}>{t('datasetCreation.stepOne.uploader.title')}</div>
{!currentFile && !file && (
<div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
<span>{t('datasetCreation.stepOne.uploader.button')}</span>
<label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
{dragging && <div ref={dragRef} className={s.draggingCover}/>}
</div>
)}
{currentFile && (
<div className={cn(s.file, uploading && s.uploading)}>
{uploading && (
<div className={s.progressbar} style={{ width: `${percent}%`}}/>
)}
<div className={cn(s.fileIcon, s[getFileType(currentFile)])}/>
<div className={s.fileInfo}>
<div className={s.filename}>
<span className={s.name}>{getFileName(currentFile.name)}</span>
<span className={s.extension}>{`.${getFileType(currentFile)}`}</span>
</div>
<div className={s.fileExtraInfo}>
<span className={s.size}>{getFileSize(currentFile.size)}</span>
<span className={s.error}></span>
</div>
</div>
<div className={s.actionWrapper}>
{uploading && (
<>
<div className={s.percent}>{`${percent}%`}</div>
<div className={s.divider}/>
<div className={s.buttonWrapper}>
<Button className={cn(s.button, 'ml-2 !h-8 bg-white')} onClick={abort}>{t('datasetCreation.stepOne.uploader.cancel')}</Button>
</div>
</>
)}
{!uploading && (
<>
<div className={s.buttonWrapper}>
<Button className={cn(s.button, 'ml-2 !h-8 bg-white')} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
<div className={s.divider}/>
<div className={s.remove} onClick={removeFile}/>
</div>
</>
)}
</div>
</div>
)}
{!currentFile && file && (
<div className={cn(s.file)}>
<div className={cn(s.fileIcon, s[file.extension])}/>
<div className={s.fileInfo}>
<div className={s.filename}>
<span className={s.name}>{getFileName(file.name)}</span>
<span className={s.extension}>{`.${file.extension}`}</span>
</div>
<div className={s.fileExtraInfo}>
<span className={s.size}>{getFileSize(file.size)}</span>
<span className={s.error}></span>
</div>
</div>
<div className={s.actionWrapper}>
<div className={s.buttonWrapper}>
<Button className={cn(s.button, 'ml-2 !h-8 bg-white')} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
<div className={s.divider}/>
<div className={s.remove} onClick={removeFile}/>
</div>
</div>
</div>
)}
<div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip')}</div>
</div>
)
}
export default FileUploader;

View File

@@ -0,0 +1,113 @@
'use client'
import React, { useState, useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import type { DataSet, File, createDocumentResponse } from '@/models/datasets'
import { fetchTenantInfo } from '@/service/common'
import { fetchDataDetail } from '@/service/datasets'
import StepsNavBar from './steps-nav-bar'
import StepOne from './step-one'
import StepTwo from './step-two'
import StepThree from './step-three'
import AccountSetting from '@/app/components/header/account-setting'
import AppUnavailable from '../../base/app-unavailable'
type DatasetUpdateFormProps = {
datasetId?: string;
}
const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
const { t } = useTranslation()
const [hasSetAPIKEY, setHasSetAPIKEY] = useState(true)
const [isShowSetAPIKey, { setTrue: showSetAPIKey, setFalse: hideSetAPIkey }] = useBoolean()
const [step, setStep] = useState(1)
const [indexingTypeCache, setIndexTypeCache] = useState('')
const [file, setFile] = useState<File | undefined>()
const [result, setResult] = useState<createDocumentResponse | undefined>()
const [hasError, setHasError] = useState(false)
const updateFile = (file?: File) => {
setFile(file)
}
const updateIndexingTypeCache = (type: string) => {
setIndexTypeCache(type)
}
const updateResultCache = (res?: createDocumentResponse) => {
setResult(res)
}
const nextStep = useCallback(() => {
setStep(step + 1)
}, [step, setStep])
const changeStep = useCallback((delta: number) => {
setStep(step + delta)
}, [step, setStep])
const checkAPIKey = async () => {
const data = await fetchTenantInfo({ url: '/info' })
const hasSetKey = data.providers.some(({ is_valid }) => is_valid)
setHasSetAPIKEY(hasSetKey)
}
useEffect(() => {
checkAPIKey()
}, [])
const [detail, setDetail] = useState<DataSet | null>(null)
useEffect(() => {
(async () => {
if (datasetId) {
try {
const detail = await fetchDataDetail(datasetId)
setDetail(detail)
} catch (e) {
setHasError(true)
}
}
})()
}, [datasetId])
if (hasError) {
return <AppUnavailable code={500} unknownReason={t('datasetCreation.error.unavailable') as string} />
}
return (
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
<div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
<StepsNavBar step={step} datasetId={datasetId} />
</div>
<div className="grow bg-white">
{step === 1 && <StepOne
datasetId={datasetId}
file={file}
updateFile={updateFile}
onStepChange={nextStep}
/>}
{(step === 2 && (!datasetId || (datasetId && !!detail))) && <StepTwo
hasSetAPIKEY={hasSetAPIKEY}
onSetting={showSetAPIKey}
indexingType={detail?.indexing_technique || ''}
datasetId={datasetId}
file={file}
onStepChange={changeStep}
updateIndexingTypeCache={updateIndexingTypeCache}
updateResultCache={updateResultCache}
/>}
{step === 3 && <StepThree
datasetId={datasetId}
datasetName={detail?.name}
indexingType={detail?.indexing_technique || indexingTypeCache}
creationCache={result}
/>}
</div>
{isShowSetAPIKey && <AccountSetting activeTab="provider" onCancel={async () => {
await checkAPIKey()
hideSetAPIkey()
}} />}
</div>
)
}
export default DatasetUpdateForm

View File

@@ -0,0 +1,109 @@
.stepHeader {
position: sticky;
top: 0;
left: 0;
padding: 42px 64px 12px;
font-weight: 600;
font-size: 18px;
line-height: 28px;
color: #101828;
}
.form {
padding: 12px 64px;
}
.dataSourceTypeList {
@apply flex items-center mb-8;
}
.dataSourceItem {
@apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
border: 0.5px solid #EAECF0;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #101828;
}
.dataSourceItem:hover {
background-color: #f5f8ff;
border: 0.5px solid #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.dataSourceItem.active {
background-color: #f5f8ff;
border: 1.5px solid #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.dataSourceItem.disabled {
background-color: #f9fafb;
border: 0.5px solid #EAECF0;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
cursor: default;
}
.dataSourceItem.disabled:hover {
background-color: #f9fafb;
border: 0.5px solid #EAECF0;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
.comingTag {
@apply flex justify-center items-center bg-white;
position: absolute;
right: 8px;
top: -10px;
padding: 1px 6px;
height: 20px;
border: 1px solid #E0EAFF;
border-radius: 6px;
font-weight: 500;
font-size: 12px;
line-height: 18px;
color: #444CE7;
}
.datasetIcon {
@apply flex mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat;
background-color: #F5FAFF;
background-image: url(../assets/file.svg);
background-size: 16px;
border: 0.5px solid #D1E9FF;
}
.dataSourceItem:active .datasetIcon,
.dataSourceItem:hover .datasetIcon {
background-color: #F5F8FF;
border: 0.5px solid #E0EAFF;
}
.datasetIcon.notion {
background-image: url(../assets/notion.svg);
background-size: 20px;
}
.datasetIcon.web {
background-image: url(../assets/web.svg);
}
.submitButton {
width: 120px;
}
.dividerLine {
margin: 32px 0;
max-width: 640px;
height: 1px;
background-color: #eaecf0;
}
.OtherCreationOption {
@apply flex items-center cursor-pointer;
font-weight: 500;
font-size: 13px;
line-height: 18px;
color: #155EEF;
}
.OtherCreationOption::before {
content: '';
display: block;
margin-right: 4px;
width: 16px;
height: 16px;
background: center no-repeat url(../assets/folder-plus.svg);
background-size: contain;
}

View File

@@ -0,0 +1,80 @@
'use client'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { File } from '@/models/datasets'
import FilePreview from '../file-preview'
import FileUploader from '../file-uploader'
import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
import Button from '@/app/components/base/button'
import cn from 'classnames'
import s from './index.module.css'
type IStepOneProps = {
datasetId?: string,
file?: File,
updateFile: (file?: File) => void,
onStepChange: () => void,
}
const StepOne = ({
datasetId,
onStepChange,
file,
updateFile,
}: IStepOneProps) => {
const [dataSourceType, setDataSourceType] = useState('FILE')
const [showModal, setShowModal] = useState(false)
const { t } = useTranslation()
const modalShowHandle = () => setShowModal(true)
const modalCloseHandle = () => setShowModal(false)
return (
<div className='flex w-full h-full'>
<div className='grow overflow-y-auto relative'>
<div className={s.stepHeader}>{t('datasetCreation.steps.one')}</div>
<div className={s.form}>
<div className={s.dataSourceTypeList}>
<div
className={cn(s.dataSourceItem, dataSourceType === 'FILE' && s.active)}
onClick={() => setDataSourceType('FILE')}
>
<span className={cn(s.datasetIcon)}/>
{t('datasetCreation.stepOne.dataSourceType.file')}
</div>
<div
className={cn(s.dataSourceItem, s.disabled, dataSourceType === 'notion' && s.active)}
// onClick={() => setDataSourceType('notion')}
>
<span className={s.comingTag}>Coming soon</span>
<span className={cn(s.datasetIcon, s.notion)}/>
{t('datasetCreation.stepOne.dataSourceType.notion')}
</div>
<div
className={cn(s.dataSourceItem, s.disabled, dataSourceType === 'web' && s.active)}
// onClick={() => setDataSourceType('web')}
>
<span className={s.comingTag}>Coming soon</span>
<span className={cn(s.datasetIcon, s.web)}/>
{t('datasetCreation.stepOne.dataSourceType.web')}
</div>
</div>
<FileUploader onFileUpdate={updateFile} file={file} />
<Button disabled={!file} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
{!datasetId && (
<>
<div className={s.dividerLine}/>
<div onClick={modalShowHandle} className={s.OtherCreationOption}>{t('datasetCreation.stepOne.emptyDatasetCreation')}</div>
</>
)}
</div>
<EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle}/>
</div>
{file && <FilePreview file={file} />}
</div>
)
}
export default StepOne

View File

@@ -0,0 +1,75 @@
.creationInfo {
padding-top: 42px;
}
.creationInfo .title {
@apply mb-2;
font-weight: 500;
font-size: 20px;
line-height: 30px;
color: #101828;
}
.creationInfo .content {
margin-bottom: 44px;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #667085;
}
.creationInfo .label {
@apply mb-2;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #101828;
}
.datasetName {
padding: 8px 12px;
background: #F9FAFB;
border-radius: 8px;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #101828;
word-break: break-all;
}
.dividerLine {
margin: 24px 0;
height: 1px;
background-color: #eaecf0;
}
.sideTip {
@apply flex flex-col items-center shrink-0 ;
padding-top: 108px;
width: 524px;
border-left: 0.5px solid #F2F4F7;
}
.tipCard {
@apply flex flex-col items-start p-6;
width: 320px;
background-color: #F9FAFB;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: 12px;
}
.tipCard .icon {
width: 32px;
height: 32px;
border: 1px solid #EAECF0;
border-radius: 6px;
background: center no-repeat url(../assets/book-open-01.svg);
background-size: 16px;
}
.tipCard .title {
margin: 12px 0;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.tipCard .content {
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #344054;
}

View File

@@ -0,0 +1,61 @@
'use client'
import React from 'react'
import { useTranslation } from 'react-i18next'
import type { createDocumentResponse } from '@/models/datasets'
import EmbeddingDetail from '../../documents/detail/embedding'
import cn from 'classnames'
import s from './index.module.css'
type StepThreeProps = {
datasetId?: string,
datasetName?: string,
indexingType?: string,
creationCache?: createDocumentResponse
}
const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
const { t } = useTranslation()
return (
<div className='flex w-full h-full'>
<div className={'h-full w-full overflow-y-scroll px-16'}>
<div className='max-w-[636px]'>
{!datasetId && (
<>
<div className={s.creationInfo}>
<div className={s.title}>{t('datasetCreation.stepThree.creationTitle')}</div>
<div className={s.content}>{t('datasetCreation.stepThree.creationContent')}</div>
<div className={s.label}>{t('datasetCreation.stepThree.label')}</div>
<div className={s.datasetName}>{datasetName || creationCache?.dataset?.name}</div>
</div>
<div className={s.dividerLine}/>
</>
)}
{datasetId && (
<div className={s.creationInfo}>
<div className={s.title}>{t('datasetCreation.stepThree.additionTitle')}</div>
<div className={s.content}>{`${t('datasetCreation.stepThree.additionP1')} ${datasetName || creationCache?.dataset?.name} ${t('datasetCreation.stepThree.additionP2')}`}</div>
</div>
)}
<EmbeddingDetail
datasetId={datasetId || creationCache?.dataset?.id}
documentId={creationCache?.document.id}
indexingType={indexingType || creationCache?.dataset?.indexing_technique}
stopPosition='bottom'
detail={creationCache?.document}
/>
</div>
</div>
<div className={cn(s.sideTip)}>
<div className={s.tipCard}>
<span className={s.icon}/>
<div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
<div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
</div>
</div>
</div>
)
}
export default StepThree;

View File

@@ -0,0 +1,382 @@
.pageHeader {
@apply px-16;
position: sticky;
top: 0;
left: 0;
padding-top: 42px;
padding-bottom: 12px;
background-color: #fff;
font-weight: 600;
font-size: 18px;
line-height: 28px;
color: #101828;
z-index: 10;
}
.fixed {
background: rgba(255, 255, 255, 0.9);
border-bottom: 0.5px solid #EAECF0;
backdrop-filter: blur(4px);
}
.form {
@apply px-16 pb-8;
}
.form .label {
@apply pt-6 pb-2;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.segmentationItem {
min-height: 68px;
}
.indexItem {
min-height: 146px;
}
.indexItem .disableMask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 12px;
z-index: 2;
}
.indexItem .warningTip {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 8px 20px 8px 40px;
background: #FFFAEB;
border-top: 0.5px solid #FEF0C7;
border-radius: 12px;
font-size: 12px;
line-height: 18px;
color: #344054;
z-index: 3;
}
.indexItem .warningTip::before {
content: '';
position: absolute;
top: 11px;
left: 20px;
width: 12px;
height: 12px;
background: center no-repeat url(../assets/alert-triangle.svg);
background-size: 12px;
}
.indexItem .warningTip .click {
color: #155EEF;
cursor: pointer;
}
.indexItem.disabled:hover {
background-color: #fcfcfd;
border-color: #f2f4f7;
box-shadow: none;
cursor: default;
}
.indexItem.disabled:hover .radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
}
.radioItem {
@apply relative mb-2 rounded-xl border border-gray-100 cursor-pointer;
background-color: #fcfcfd;
}
.radioItem.segmentationItem.custom {
height: auto;
}
.radioItem.segmentationItem.custom .typeHeader {
/* height: 65px; */
}
.radioItem.indexItem .typeHeader {
@apply py-4 pr-5;
}
.radioItem.indexItem.active .typeHeader {
padding: 15.5px 19.5px 15.5px 63.5px;
}
.radioItem.indexItem .radio {
top: 16px;
right: 20px;
}
.radioItem.indexItem.active .radio {
top: 16px;
right: 19.5px;
}
.radioItem.indexItem .typeHeader .title {
@apply pb-1;
}
.radioItem.indexItem .typeHeader .tip {
@apply pb-3;
}
.radioItem .typeIcon {
position: absolute;
top: 18px;
left: 20px;
width: 32px;
height: 32px;
background: #EEF4FF center no-repeat;
border-radius: 8px;
}
.typeIcon.auto {
background-color: #F5F3FF;
background-image: url(../assets/zap-fast.svg);
}
.typeIcon.customize {
background-image: url(../assets/sliders-02.svg);
}
.typeIcon.qualified {
background-color: #FFF6ED;
background-image: url(../assets/star-07.svg);
}
.typeIcon.economical {
background-image: url(../assets/piggy-bank-01.svg);
}
.radioItem .radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
position: absolute;
top: 26px;
right: 20px;
}
.radioItem:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.radioItem:hover .radio {
border-color: #155eef;
}
.radioItem.active {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.radioItem.active .radio {
top: 25.5px;
right: 19.5px;
border-width: 5px;
border-color: #155EEF;
}
.radioItem.active:hover {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.radioItem.active .typeIcon {
top: 17.5px;
left: 19.5px;
}
.radioItem.active .typeHeader {
padding: 11.5px 63.5px;
}
.typeHeader {
@apply flex flex-col px-16 py-3 justify-center;
}
.typeHeader .title {
display: flex;
align-items: center;
padding-bottom: 2px;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #101828;
}
.typeHeader .tip {
font-weight: 400;
font-size: 13px;
line-height: 18px;
color: #667085;
}
.recommendTag {
display: inline-flex;
justify-content: center;
align-items: center;
padding: 0 6px;
margin-left: 4px;
border: 1px solid #E0EAFF;
border-radius: 6px;
font-weight: 500;
font-size: 12px;
line-height: 20px;
color: #444CE7;
}
.typeFormBody {
@apply px-16;
border-top: 1px solid #F2F4F7;
}
.formRow {
@apply flex justify-between mt-6;
}
.formRow .label {
@apply mb-2 p-0;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #101828;
}
.ruleItem {
@apply flex items-center h-7;
}
.formFooter {
padding: 16px 0 28px;
}
.formFooter .button {
font-size: 13px;
line-height: 18px;
}
.input {
@apply inline-flex h-9 w-full py-1 px-2 rounded-lg text-xs leading-normal;
@apply bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-white placeholder:text-gray-400;
}
.file {
@apply flex justify-between items-center mt-8 px-6 py-4 rounded-xl bg-gray-50;
}
.file .divider {
@apply shrink-0 mx-4 w-px bg-gray-200;
height: 42px;
}
.fileIcon {
@apply inline-flex mr-1 w-6 h-6 bg-center bg-no-repeat;
background-image: url(../assets/pdf.svg);
background-size: 24px;
}
.fileIcon.pdf {
background-image: url(../assets/pdf.svg);
}
.fileIcon.html,
.fileIcon.htm {
background-image: url(../assets/html.svg);
}
.fileIcon.md,
.fileIcon.markdown {
background-image: url(../assets/md.svg);
}
.fileIcon.txt {
background-image: url(../assets/txt.svg);
}
.fileIcon.json {
background-image: url(../assets/json.svg);
}
.fileContent {
flex: 1 1 50%;
}
.divider {
@apply mx-3 w-px h-4 bg-gray-200;
}
.calculating {
color: #98A2B3;
font-size: 12px;
line-height: 18px;
}
.sideTip {
@apply flex flex-col items-center shrink-0;
padding-top: 108px;
width: 524px;
border-left: 0.5px solid #F2F4F7;
}
.tipCard {
@apply flex flex-col items-start p-6;
width: 320px;
background-color: #F9FAFB;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: 12px;
}
.tipCard .icon {
width: 32px;
height: 32px;
border: 1px solid #EAECF0;
border-radius: 6px;
background: center no-repeat url(../assets/book-open-01.svg);
background-size: 16px;
}
.tipCard .title {
margin: 12px 0;
font-weight: 500;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.tipCard .content {
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #344054;
}
.previewWrap {
flex-shrink: 0;
width: 524px;
}
.previewHeader {
position: sticky;
top: 0;
left: 0;
padding-top: 42px;
background-color: #fff;
font-weight: 600;
font-size: 18px;
line-height: 28px;
color: #101828;
z-index: 10;
}

View File

@@ -0,0 +1,491 @@
'use client'
import React, { useState, useRef, useEffect, useLayoutEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import type { File, PreProcessingRule, Rules, FileIndexingEstimateResponse as IndexingEstimateResponse } from '@/models/datasets'
import {
fetchDefaultProcessRule,
createFirstDocument,
createDocument,
fetchFileIndexingEstimate as didFetchFileIndexingEstimate,
} from '@/service/datasets'
import type { CreateDocumentReq, createDocumentResponse } from '@/models/datasets'
import Button from '@/app/components/base/button'
import PreviewItem from './preview-item'
import Loading from '@/app/components/base/loading'
import { XMarkIcon } from '@heroicons/react/20/solid'
import cn from 'classnames'
import s from './index.module.css'
import Link from 'next/link'
import Toast from '@/app/components/base/toast'
import { formatNumber } from '@/utils/format'
type StepTwoProps = {
hasSetAPIKEY: boolean,
onSetting: () => void,
datasetId?: string,
indexingType?: string,
file?: File,
onStepChange: (delta: number) => void,
updateIndexingTypeCache: (type: string) => void,
updateResultCache: (res: createDocumentResponse) => void
}
enum SegmentType {
AUTO = 'automatic',
CUSTOM = 'custom',
}
enum IndexingType {
QUALIFIED = 'high_quality',
ECONOMICAL = 'economy',
}
const StepTwo = ({
hasSetAPIKEY,
onSetting,
datasetId,
indexingType,
file,
onStepChange,
updateIndexingTypeCache,
updateResultCache,
}: StepTwoProps) => {
const { t } = useTranslation()
const scrollRef = useRef<HTMLDivElement>(null)
const [scrolled, setScrolled] = useState(false)
const previewScrollRef = useRef<HTMLDivElement>(null)
const [previewScrolled, setPreviewScrolled] = useState(false)
const [segmentationType, setSegmentationType] = useState<SegmentType>(SegmentType.AUTO)
const [segmentIdentifier, setSegmentIdentifier] = useState('\\n')
const [max, setMax] = useState(1000)
const [rules, setRules] = useState<PreProcessingRule[]>([])
const [defaultConfig, setDefaultConfig] = useState<Rules>()
const hasSetIndexType = !!indexingType
const [indexType, setIndexType] = useState<IndexingType>(
indexingType ||
hasSetAPIKEY ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL
)
const [showPreview, { setTrue: setShowPreview, setFalse: hidePreview }] = useBoolean()
const [customFileIndexingEstimate, setCustomFileIndexingEstimate] = useState<IndexingEstimateResponse | null>(null)
const [automaticFileIndexingEstimate, setAutomaticFileIndexingEstimate] = useState<IndexingEstimateResponse | null>(null)
const fileIndexingEstimate = (() => {
return segmentationType === SegmentType.AUTO ? automaticFileIndexingEstimate : customFileIndexingEstimate
})()
const scrollHandle = (e: any) => {
if (e.target.scrollTop > 0) {
setScrolled(true)
} else {
setScrolled(false)
}
}
const previewScrollHandle = (e: any) => {
if (e.target.scrollTop > 0) {
setPreviewScrolled(true)
} else {
setPreviewScrolled(false)
}
}
const getFileName = (name: string) => {
const arr = name.split('.')
return arr.slice(0, -1).join('.')
}
const getRuleName = (key: string) => {
if (key === 'remove_extra_spaces') {
return t('datasetCreation.stepTwo.removeExtraSpaces')
}
if (key === 'remove_urls_emails') {
return t('datasetCreation.stepTwo.removeUrlEmails')
}
if (key === 'remove_stopwords') {
return t('datasetCreation.stepTwo.removeStopwords')
}
}
const ruleChangeHandle = (id: string) => {
const newRules = rules.map(rule => {
if (rule.id === id) {
return {
id: rule.id,
enabled: !rule.enabled,
}
}
return rule
})
setRules(newRules)
}
const resetRules = () => {
if (defaultConfig) {
setSegmentIdentifier(defaultConfig.segmentation.separator === '\n' ? '\\n' : defaultConfig.segmentation.separator || '\\n')
setMax(defaultConfig.segmentation.max_tokens)
setRules(defaultConfig.pre_processing_rules)
}
}
const confirmChangeCustomConfig = async () => {
setCustomFileIndexingEstimate(null)
setShowPreview()
await fetchFileIndexingEstimate()
}
const getIndexing_technique = () => indexingType ? indexingType : indexType
const getProcessRule = () => {
const processRule: any = {
rules: {}, // api will check this. It will be removed after api refactored.
mode: segmentationType,
}
if (segmentationType === SegmentType.CUSTOM) {
const ruleObj = {
pre_processing_rules: rules,
segmentation: {
separator: segmentIdentifier === '\\n' ? '\n' : segmentIdentifier,
max_tokens: max,
},
}
processRule.rules = ruleObj
}
return processRule
}
const getFileIndexingEstimateParams = () => {
const params = {
file_id: file?.id,
dataset_id: datasetId,
indexing_technique: getIndexing_technique(),
process_rule: getProcessRule(),
}
return params
}
const fetchFileIndexingEstimate = async () => {
const res = await didFetchFileIndexingEstimate(getFileIndexingEstimateParams())
if (segmentationType === SegmentType.CUSTOM) {
setCustomFileIndexingEstimate(res)
}
else {
setAutomaticFileIndexingEstimate(res)
}
}
const getCreationParams = () => {
const params = {
data_source: {
type: 'upload_file',
info: file?.id,
name: file?.name,
},
indexing_technique: getIndexing_technique(),
process_rule: getProcessRule(),
} as CreateDocumentReq
return params
}
const getRules = async () => {
try {
const res = await fetchDefaultProcessRule({ url: '/datasets/process-rule' })
const separator = res.rules.segmentation.separator
setSegmentIdentifier(separator === '\n' ? '\\n' : separator || '\\n')
setMax(res.rules.segmentation.max_tokens)
setRules(res.rules.pre_processing_rules)
setDefaultConfig(res.rules)
}
catch (err) {
console.log(err)
}
}
const createHandle = async () => {
try {
let res;
const params = getCreationParams()
if (!datasetId) {
res = await createFirstDocument({
body: params
})
updateIndexingTypeCache(indexType)
updateResultCache(res)
} else {
res = await createDocument({
datasetId,
body: params
})
updateIndexingTypeCache(indexType)
updateResultCache({
document: res,
})
}
onStepChange(+1)
}
catch (err) {
Toast.notify({
type: 'error',
message: err + '',
})
}
}
useEffect(() => {
// fetch rules
getRules()
}, [])
useEffect(() => {
scrollRef.current?.addEventListener('scroll', scrollHandle);
return () => {
scrollRef.current?.removeEventListener('scroll', scrollHandle);
}
}, [])
useLayoutEffect(() => {
if (showPreview) {
previewScrollRef.current?.addEventListener('scroll', previewScrollHandle);
return () => {
previewScrollRef.current?.removeEventListener('scroll', previewScrollHandle);
}
}
}, [showPreview])
useEffect(() => {
// get indexing type by props
if (indexingType) {
setIndexType(indexingType as IndexingType)
} else {
setIndexType(hasSetAPIKEY ? IndexingType.QUALIFIED : IndexingType.ECONOMICAL)
}
}, [hasSetAPIKEY, indexingType, datasetId])
useEffect(() => {
if (segmentationType === SegmentType.AUTO) {
setAutomaticFileIndexingEstimate(null)
setShowPreview()
fetchFileIndexingEstimate()
} else {
hidePreview()
setCustomFileIndexingEstimate(null)
}
}, [segmentationType, indexType])
return (
<div className='flex w-full h-full'>
<div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
<div className={cn(s.pageHeader, scrolled && s.fixed)}>{t('datasetCreation.steps.two')}</div>
<div className={cn(s.form)}>
<div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
<div className='max-w-[640px]'>
<div
className={cn(
s.radioItem,
s.segmentationItem,
segmentationType === SegmentType.AUTO && s.active
)}
onClick={() => setSegmentationType(SegmentType.AUTO)}
>
<span className={cn(s.typeIcon, s.auto)} />
<span className={cn(s.radio)} />
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.auto')}</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.autoDescription')}</div>
</div>
</div>
<div
className={cn(
s.radioItem,
s.segmentationItem,
segmentationType === SegmentType.CUSTOM && s.active,
segmentationType === SegmentType.CUSTOM && s.custom,
)}
onClick={() => setSegmentationType(SegmentType.CUSTOM)}
>
<span className={cn(s.typeIcon, s.customize)} />
<span className={cn(s.radio)} />
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.custom')}</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.customDescription')}</div>
</div>
{segmentationType === SegmentType.CUSTOM && (
<div className={s.typeFormBody}>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>{t('datasetCreation.stepTwo.separator')}</div>
<input
type="text"
className={s.input}
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={segmentIdentifier}
onChange={(e) => setSegmentIdentifier(e.target.value)}
/>
</div>
</div>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>{t('datasetCreation.stepTwo.maxLength')}</div>
<input
type="number"
className={s.input}
placeholder={t('datasetCreation.stepTwo.separatorPlaceholder') || ''} value={max}
onChange={(e) => setMax(Number(e.target.value))}
/>
</div>
</div>
<div className={s.formRow}>
<div className='w-full'>
<div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem}>
<input id={rule.id} type="checkbox" defaultChecked={rule.enabled} onChange={() => ruleChangeHandle(rule.id)} className="w-4 h-4 rounded border-gray-300 text-blue-700 focus:ring-blue-700" />
<label htmlFor={rule.id} className="ml-2 text-sm font-normal cursor-pointer text-gray-800">{getRuleName(rule.id)}</label>
</div>
))}
</div>
</div>
<div className={s.formFooter}>
<Button type="primary" className={cn(s.button, '!h-8 text-primary-600')} onClick={confirmChangeCustomConfig}>{t('datasetCreation.stepTwo.preview')}</Button>
<Button className={cn(s.button, 'ml-2 !h-8')} onClick={resetRules}>{t('datasetCreation.stepTwo.reset')}</Button>
</div>
</div>
)}
</div>
</div>
<div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
<div className='max-w-[640px]'>
<div className='flex items-center gap-3'>
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
<div
className={cn(
s.radioItem,
s.indexItem,
!hasSetAPIKEY && s.disabled,
!hasSetIndexType && indexType === IndexingType.QUALIFIED && s.active,
hasSetIndexType && s.disabled,
hasSetIndexType && '!w-full',
)}
onClick={() => {
if (hasSetAPIKEY) {
setIndexType(IndexingType.QUALIFIED)
}
}}
>
<span className={cn(s.typeIcon, s.qualified)} />
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>
{t('datasetCreation.stepTwo.qualified')}
{!hasSetIndexType && <span className={s.recommendTag}>{t('datasetCreation.stepTwo.recommend')}</span>}
</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.qualifiedTip')}</div>
<div className='pb-0.5 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateCost')}</div>
{
!!fileIndexingEstimate ? (
<div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.tokens)} tokens(<span className='text-yellow-500'>${formatNumber(fileIndexingEstimate.total_price)}</span>)</div>
) : (
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
)
}
</div>
{!hasSetAPIKEY && (
<div className={s.warningTip}>
<span>{t('datasetCreation.stepTwo.warning')}&nbsp;</span>
<span className={s.click} onClick={onSetting}>{t('datasetCreation.stepTwo.click')}</span>
</div>
)}
</div>
)}
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.ECONOMICAL)) && (
<div
className={cn(
s.radioItem,
s.indexItem,
!hasSetIndexType && indexType === IndexingType.ECONOMICAL && s.active,
hasSetIndexType && s.disabled,
hasSetIndexType && '!w-full',
)}
onClick={() => !hasSetIndexType && setIndexType(IndexingType.ECONOMICAL)}
>
<span className={cn(s.typeIcon, s.economical)} />
{!hasSetIndexType && <span className={cn(s.radio)} />}
<div className={s.typeHeader}>
<div className={s.title}>{t('datasetCreation.stepTwo.economical')}</div>
<div className={s.tip}>{t('datasetCreation.stepTwo.economicalTip')}</div>
<div className='pb-0.5 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateCost')}</div>
<div className='text-xs font-medium text-gray-800'>0 tokens</div>
</div>
</div>
)}
</div>
{hasSetIndexType && (
<div className='mt-2 text-xs text-gray-500 font-medium'>
{t('datasetCreation.stepTwo.indexSettedTip')}
<Link className='text-[#155EEF]' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
</div>
)}
<div className={s.file}>
<div className={s.fileContent}>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.fileName')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
<span className={cn(s.fileIcon, file && s[file.extension])} />
{getFileName(file?.name || '')}
</div>
</div>
<div className={s.divider} />
<div className={s.fileContent}>
<div className='mb-2 text-xs font-medium text-gray-500'>{t('datasetCreation.stepTwo.emstimateSegment')}</div>
<div className='flex items-center text-sm leading-6 font-medium text-gray-800'>
{
!!fileIndexingEstimate ? (
<div className='text-xs font-medium text-gray-800'>{formatNumber(fileIndexingEstimate.total_segments)} </div>
) : (
<div className={s.calculating}>{t('datasetCreation.stepTwo.calculating')}</div>
)
}
</div>
</div>
</div>
<div className='flex items-center mt-8 py-2'>
<Button onClick={() => onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button>
<div className={s.divider} />
<Button type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
</div>
</div>
</div>
</div>
{(showPreview) ? (
<div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}>
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`, ' flex items-center justify-between px-8')}>
<span>{t('datasetCreation.stepTwo.previewTitle')}</span>
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
</div>
<div className='my-4 px-8 space-y-4'>
{fileIndexingEstimate?.preview ? (
<>
{fileIndexingEstimate?.preview.map((item, index) => (
<PreviewItem key={item} content={item} index={index + 1} />
))}
</>
) : <div className='flex items-center justify-center h-[200px]'><Loading type='area'></Loading></div>
}
</div>
</div>
) :
(<div className={cn(s.sideTip)}>
<div className={s.tipCard}>
<span className={s.icon} />
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
<div className={s.content}>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
<p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
</div>
</div>
</div>)}
</div>
)
}
export default StepTwo

View File

@@ -0,0 +1,49 @@
'use client'
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
export interface IPreviewItemProps {
index: number
content: string
}
const sharpIcon = (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.74999 1.5L3.24999 10.5M8.74998 1.5L7.24998 10.5M10.25 4H1.75M9.75 8H1.25" stroke="#98A2B3" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
const textIcon = (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 3.5H8M6 3.5V8.5M3.9 10.5H8.1C8.94008 10.5 9.36012 10.5 9.68099 10.3365C9.96323 10.1927 10.1927 9.96323 10.3365 9.68099C10.5 9.36012 10.5 8.94008 10.5 8.1V3.9C10.5 3.05992 10.5 2.63988 10.3365 2.31901C10.1927 2.03677 9.96323 1.8073 9.68099 1.66349C9.36012 1.5 8.94008 1.5 8.1 1.5H3.9C3.05992 1.5 2.63988 1.5 2.31901 1.66349C2.03677 1.8073 1.8073 2.03677 1.66349 2.31901C1.5 2.63988 1.5 3.05992 1.5 3.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5Z" stroke="#667085" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
const PreviewItem: FC<IPreviewItemProps> = ({
index,
content,
}) => {
const { t } = useTranslation()
const charNums = (content || '').length
const formatedIndex = (() => (index + '').padStart(3, '0'))()
return (
<div className='p-4 rounded-xl bg-gray-50'>
<div className='flex items-center justify-between h-5 text-xs text-gray-500'>
<div className='flex items-center h-[18px] space-x-1 border border-gray-200 box-border rounded-md italic pl-1 pr-1.5 font-medium'>
{sharpIcon}
<span>{formatedIndex}</span>
</div>
<div className='flex items-center space-x-1'>
{textIcon}
<span>{charNums} {t('datasetCreation.stepTwo.characters')}</span>
</div>
</div>
<div className='mt-2 max-h-[120px] line-clamp-6 overflow-hidden text-sm text-gray-800'>
{content}
</div>
</div>
)
}
export default React.memo(PreviewItem)

View File

@@ -0,0 +1,106 @@
.stepsHeader {
@apply flex items-center px-6 py-6;
color: #344054;
font-weight: 600;
font-size: 14px;
line-height: 20px;
}
.navBack {
@apply box-border flex justify-center items-center mr-3 w-8 h-8 bg-white bg-center bg-no-repeat cursor-pointer hover:border-gray-300;
border: 0.5px solid #F2F4F7;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
border-radius: 32px;
background-image: url(../assets/arrow-narrow-left.svg);
background-size: 16px;
}
.stepList {
@apply p-4;
line-height: 18px;
}
.stepItem {
@apply relative flex justify-items-start pt-3 pr-0 pb-3;
padding-left: 52px;
font-size: 13px;
}
.stepItem.step1::before {
content: '';
position: absolute;
bottom: 0;
left: 23px;
width: 2px;
height: 7px;
background-color: #f2f4f7;
}
.stepItem.step2::before {
content: '';
position: absolute;
top: 0;
left: 23px;
width: 2px;
height: 100%;
background-color: #f2f4f7;
}
.stepItem.step2::after {
content: '';
position: absolute;
top: 6px;
left: 23px;
width: 2px;
height: 28px;
background-color: #fff;
}
.stepItem.step3::before {
content: '';
position: absolute;
top: 0;
left: 23px;
width: 2px;
height: 7px;
background-color: #f2f4f7;
}
.stepNum {
@apply box-border absolute top-2 left-3 flex justify-center items-center w-6 h-6;
color: #98a2b3;
font-size: 12px;
border: 1px solid #F2F4F7;
border-radius: 24px;
z-index: 1;
}
.stepName {
color: #98a2b3;
}
.stepItem.active .stepNum {
color: #1c64f2;
background-color: #EFF4FF;
border: none;
}
.stepItem.active .stepName {
color: #1c64f2;
}
.stepItem.done .stepNum {
color: #667085;
background-color: #f2f4f7;
border: none;
}
.stepItem.done .stepNum::after {
content: '';
display: flex;
width: 12px;
height: 12px;
background: center no-repeat url(../assets/check.svg);
background-size: 12px;
}
.stepItem.done .stepName {
color: #667085;
}

View File

@@ -0,0 +1,51 @@
'use client'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import cn from 'classnames'
import s from './index.module.css'
type IStepsNavBarProps = {
step: number,
datasetId?: string,
}
const StepsNavBar = ({
step,
datasetId,
}: IStepsNavBarProps) => {
const { t } = useTranslation()
const router = useRouter()
const navBackHandle = () => {
if (!datasetId) {
router.replace('/datasets')
} else {
router.replace(`/datasets/${datasetId}/documents`)
}
}
return (
<div className='w-full pt-4'>
<div className={s.stepsHeader}>
<div onClick={navBackHandle} className={s.navBack} />
{!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update')}
</div>
<div className={cn(s.stepList)}>
<div className={cn(s.stepItem, s.step1, step === 1 && s.active, step !== 1 && s.done)}>
<div className={cn(s.stepNum)}>{step === 1 ? 1 : ''}</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.one')}</div>
</div>
<div className={cn(s.stepItem, s.step2, step === 2 && s.active, step === 3 && s.done)}>
<div className={cn(s.stepNum)}>{step !== 3 ? 2 : ''}</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.two')}</div>
</div>
<div className={cn(s.stepItem, s.step3, step === 3 && s.active)}>
<div className={cn(s.stepNum)}>3</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.three')}</div>
</div>
</div>
</div>
)
}
export default StepsNavBar

View File

@@ -0,0 +1,37 @@
.modal {
position: relative;
}
.modal .icon {
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.9) center no-repeat url(../assets/annotation-info.svg);
background-size: 24px;
border: 0.5px solid #F2F4F7;
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08), 0px 8px 8px -4px rgba(16, 24, 40, 0.03);
border-radius: 12px;
}
.modal .close {
position: absolute;
right: 16px;
top: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
background: center no-repeat url(../assets/close.svg);
background-size: 16px;
cursor: pointer;
}
.modal .title {
@apply mt-3 mb-1;
font-weight: 600;
font-size: 20px;
line-height: 30px;
color: #101828;
}
.modal .content {
@apply mb-10;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #667085;
}

View File

@@ -0,0 +1,46 @@
'use client'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import cn from 'classnames'
import s from './index.module.css'
type IProps = {
show: boolean,
onConfirm: () => void,
onHide: () => void,
}
const StopEmbeddingModal = ({
show = false,
onConfirm,
onHide,
}: IProps) => {
const { t } = useTranslation()
const submit = () => {
onConfirm()
onHide()
}
return (
<Modal
isShow={show}
onClose={onHide}
className={cn(s.modal, '!max-w-[480px]', 'px-8')}
>
<div className={s.icon}/>
<span className={s.close} onClick={onHide}/>
<div className={s.title}>{t('datasetCreation.stepThree.modelTitle')}</div>
<div className={s.content}>{t('datasetCreation.stepThree.modelContent')}</div>
<div className='flex flex-row-reverse'>
<Button className='w-24 ml-2' type='primary' onClick={submit}>{t('datasetCreation.stepThree.modelButtonConfirm')}</Button>
<Button className='w-24' onClick={onHide}>{t('datasetCreation.stepThree.modelButtonCancel')}</Button>
</div>
</Modal>
)
}
export default StopEmbeddingModal

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 8.00008C6.66667 7.2637 7.26362 6.66675 8 6.66675C8.73638 6.66675 9.33333 7.2637 9.33333 8.00008C9.33333 8.73646 8.73638 9.33341 8 9.33341C7.26362 9.33341 6.66667 8.73646 6.66667 8.00008Z" fill="#667085" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3333 8.00008C11.3333 7.2637 11.9303 6.66675 12.6667 6.66675C13.403 6.66675 14 7.2637 14 8.00008C14 8.73646 13.403 9.33341 12.6667 9.33341C11.9303 9.33341 11.3333 8.73646 11.3333 8.00008Z" fill="#667085" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 8.00008C2 7.2637 2.59695 6.66675 3.33333 6.66675C4.06971 6.66675 4.66667 7.2637 4.66667 8.00008C4.66667 8.73646 4.06971 9.33341 3.33333 9.33341C2.59695 9.33341 2 8.73646 2 8.00008Z" fill="#667085" />
</svg>

After

Width:  |  Height:  |  Size: 892 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2427_26665)">
<path d="M10.6668 5.33333V8.66666C10.6668 9.19709 10.8776 9.7058 11.2526 10.0809C11.6277 10.4559 12.1364 10.6667 12.6668 10.6667C13.1973 10.6667 13.706 10.4559 14.0811 10.0809C14.4561 9.7058 14.6668 9.19709 14.6668 8.66666V7.99999C14.6667 6.49535 14.1577 5.03498 13.2224 3.85635C12.287 2.67772 10.9805 1.85014 9.51526 1.50819C8.04999 1.16624 6.51213 1.33002 5.15173 1.9729C3.79134 2.61579 2.68843 3.69996 2.02234 5.04914C1.35625 6.39832 1.16615 7.93315 1.48295 9.40407C1.79975 10.875 2.60482 12.1955 3.76726 13.1508C4.92969 14.1062 6.38112 14.6402 7.88555 14.6661C9.38997 14.692 10.8589 14.2082 12.0535 13.2933M10.6668 7.99999C10.6668 9.47275 9.47293 10.6667 8.00017 10.6667C6.52741 10.6667 5.3335 9.47275 5.3335 7.99999C5.3335 6.52723 6.52741 5.33333 8.00017 5.33333C9.47293 5.33333 10.6668 6.52723 10.6668 7.99999Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_2427_26665">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.42857 3.5L2.57143 8.5M3 9.5H8.9999M9.42857 8.5L6.57143 3.5M1.8 10.5H2.2C2.48003 10.5 2.62004 10.5 2.727 10.4455C2.82108 10.3976 2.89757 10.3211 2.9455 10.227C3 10.12 3 9.98003 3 9.7V9.3C3 9.01997 3 8.87996 2.9455 8.773C2.89757 8.67892 2.82108 8.60243 2.727 8.5545C2.62004 8.5 2.48003 8.5 2.2 8.5H1.8C1.51997 8.5 1.37996 8.5 1.273 8.5545C1.17892 8.60243 1.10243 8.67892 1.0545 8.773C1 8.87996 1 9.01997 1 9.3V9.7C1 9.98003 1 10.12 1.0545 10.227C1.10243 10.3211 1.17892 10.3976 1.273 10.4455C1.37996 10.5 1.51997 10.5 1.8 10.5ZM9.8 10.5H10.2C10.48 10.5 10.62 10.5 10.727 10.4455C10.8211 10.3976 10.8976 10.3211 10.9455 10.227C11 10.12 11 9.98003 11 9.7V9.3C11 9.01997 11 8.87996 10.9455 8.773C10.8976 8.67892 10.8211 8.60243 10.727 8.5545C10.62 8.5 10.48 8.5 10.2 8.5H9.8C9.51997 8.5 9.37996 8.5 9.273 8.5545C9.17892 8.60243 9.10243 8.67892 9.0545 8.773C9 8.87996 9 9.01997 9 9.3V9.7C9 9.98003 9 10.12 9.0545 10.227C9.10243 10.3211 9.17892 10.3976 9.273 10.4455C9.37996 10.5 9.51997 10.5 9.8 10.5ZM5.8 3.5H6.2C6.48003 3.5 6.62004 3.5 6.727 3.4455C6.82108 3.39757 6.89757 3.32108 6.9455 3.227C7 3.12004 7 2.98003 7 2.7V2.3C7 2.01997 7 1.87996 6.9455 1.773C6.89757 1.67892 6.82108 1.60243 6.727 1.5545C6.62004 1.5 6.48003 1.5 6.2 1.5H5.8C5.51997 1.5 5.37996 1.5 5.273 1.5545C5.17892 1.60243 5.10243 1.67892 5.0545 1.773C5 1.87996 5 2.01997 5 2.3V2.7C5 2.98003 5 3.12004 5.0545 3.227C5.10243 3.32108 5.17892 3.39757 5.273 3.4455C5.37996 3.5 5.51997 3.5 5.8 3.5Z" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00016 14L7.93346 13.8999C7.47037 13.2053 7.23882 12.858 6.9329 12.6065C6.66207 12.3839 6.35001 12.2169 6.01457 12.1151C5.63566 12 5.21823 12 4.38338 12H3.46683C2.72009 12 2.34672 12 2.06151 11.8547C1.81063 11.7268 1.60665 11.5229 1.47882 11.272C1.3335 10.9868 1.3335 10.6134 1.3335 9.86667V4.13333C1.3335 3.3866 1.3335 3.01323 1.47882 2.72801C1.60665 2.47713 1.81063 2.27316 2.06151 2.14532C2.34672 2 2.72009 2 3.46683 2H3.7335C5.22697 2 5.97371 2 6.54414 2.29065C7.0459 2.54631 7.45385 2.95426 7.70951 3.45603C8.00016 4.02646 8.00016 4.77319 8.00016 6.26667M8.00016 14V6.26667M8.00016 14L8.06687 13.8999C8.52996 13.2053 8.76151 12.858 9.06743 12.6065C9.33826 12.3839 9.65032 12.2169 9.98576 12.1151C10.3647 12 10.7821 12 11.6169 12H12.5335C13.2802 12 13.6536 12 13.9388 11.8547C14.1897 11.7268 14.3937 11.5229 14.5215 11.272C14.6668 10.9868 14.6668 10.6134 14.6668 9.86667V4.13333C14.6668 3.3866 14.6668 3.01323 14.5215 2.72801C14.3937 2.47713 14.1897 2.27316 13.9388 2.14532C13.6536 2 13.2802 2 12.5335 2H12.2668C10.7734 2 10.0266 2 9.45619 2.29065C8.95442 2.54631 8.54647 2.95426 8.29081 3.45603C8.00016 4.02646 8.00016 4.77319 8.00016 6.26667" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.3335 14V4.66667C5.3335 4.04669 5.3335 3.7367 5.40164 3.48236C5.58658 2.79218 6.12567 2.25308 6.81586 2.06815C7.07019 2 7.38018 2 8.00016 2C8.62014 2 8.93013 2 9.18447 2.06815C9.87465 2.25308 10.4137 2.79218 10.5987 3.48236C10.6668 3.7367 10.6668 4.04669 10.6668 4.66667V14M3.46683 14H12.5335C13.2802 14 13.6536 14 13.9388 13.8547C14.1897 13.7268 14.3937 13.5229 14.5215 13.272C14.6668 12.9868 14.6668 12.6134 14.6668 11.8667V6.8C14.6668 6.05326 14.6668 5.6799 14.5215 5.39468C14.3937 5.1438 14.1897 4.93982 13.9388 4.81199C13.6536 4.66667 13.2802 4.66667 12.5335 4.66667H3.46683C2.72009 4.66667 2.34672 4.66667 2.06151 4.81199C1.81063 4.93982 1.60665 5.1438 1.47882 5.39468C1.3335 5.6799 1.3335 6.05326 1.3335 6.8V11.8667C1.3335 12.6134 1.3335 12.9868 1.47882 13.272C1.60665 13.5229 1.81063 13.7268 2.06151 13.8547C2.34672 14 2.72009 14 3.46683 14Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,15 @@
<svg width="232" height="120" viewBox="0 0 232 120" preserveAspectRatio="none" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="6" width="232" height="8" rx="3" fill="#EAECF0"/>
<rect y="26" width="232" height="8" rx="3" fill="#EAECF0"/>
<rect y="46" width="232" height="8" rx="3" fill="#EAECF0"/>
<rect y="66" width="232" height="8" rx="3" fill="#EAECF0"/>
<rect y="86" width="232" height="8" rx="3" fill="#EAECF0"/>
<g clip-path="url(#clip0_2368_22256)">
<rect y="106" width="256" height="8" rx="3" fill="#EAECF0"/>
</g>
<defs>
<clipPath id="clip0_2368_22256">
<rect width="232" height="20" fill="white" transform="translate(0 100)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 672 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.33317 1.51306V4.26676C9.33317 4.64012 9.33317 4.82681 9.40583 4.96942C9.46975 5.09486 9.57173 5.19684 9.69718 5.26076C9.83978 5.33342 10.0265 5.33342 10.3998 5.33342H13.1535M10.6665 8.66671H5.33317M10.6665 11.3334H5.33317M6.6665 6.00004H5.33317M9.33317 1.33337H5.8665C4.7464 1.33337 4.18635 1.33337 3.75852 1.55136C3.3822 1.74311 3.07624 2.04907 2.88449 2.42539C2.6665 2.85322 2.6665 3.41327 2.6665 4.53337V11.4667C2.6665 12.5868 2.6665 13.1469 2.88449 13.5747C3.07624 13.951 3.3822 14.257 3.75852 14.4487C4.18635 14.6667 4.7464 14.6667 5.8665 14.6667H10.1332C11.2533 14.6667 11.8133 14.6667 12.2412 14.4487C12.6175 14.257 12.9234 13.951 13.1152 13.5747C13.3332 13.1469 13.3332 12.5868 13.3332 11.4667V5.33337L9.33317 1.33337Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 929 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2427_26661)">
<path d="M1.79133 10.4301L3.06346 9.69574C3.13237 9.65596 3.21323 9.64214 3.29143 9.65678L5.79443 10.1252C6.00013 10.1637 6.19001 10.0054 6.18908 9.7961L6.17934 7.60301C6.17907 7.54342 6.19478 7.48486 6.22484 7.43341L7.48799 5.27085C7.55373 5.1583 7.54784 5.01776 7.47291 4.91111L5.34611 1.88383M12.667 3.23941C9.0003 5.00007 11.0002 7.3334 11.667 7.66674C12.9184 8.29232 14.6586 8.33337 14.6586 8.33337C14.664 8.22294 14.6668 8.11182 14.6668 8.00004C14.6668 4.31814 11.6821 1.33337 8.00016 1.33337C4.31826 1.33337 1.3335 4.31814 1.3335 8.00004C1.3335 11.6819 4.31826 14.6667 8.00016 14.6667C8.11194 14.6667 8.22307 14.664 8.3335 14.6585M11.172 14.6266L9.06083 9.06071L14.6267 11.1719L12.1586 12.1585L11.172 14.6266Z" stroke="#155EEF" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_2427_26661">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.3335 9.66667V7.66295C11.3335 7.5433 11.3335 7.48348 11.3153 7.43066C11.2992 7.38395 11.2729 7.34141 11.2383 7.30611C11.1992 7.26619 11.1457 7.23944 11.0387 7.18593L8.00016 5.66667M2.66683 6.33334V10.8711C2.66683 11.119 2.66683 11.243 2.70551 11.3515C2.7397 11.4475 2.79543 11.5343 2.86842 11.6054C2.95097 11.6858 3.06368 11.7374 3.28906 11.8408L7.55573 13.7963C7.71922 13.8712 7.80097 13.9087 7.88612 13.9235C7.96159 13.9366 8.03874 13.9366 8.1142 13.9235C8.19936 13.9087 8.28111 13.8712 8.44459 13.7963L12.7113 11.8408C12.9367 11.7374 13.0494 11.6858 13.1319 11.6054C13.2049 11.5343 13.2606 11.4475 13.2948 11.3515C13.3335 11.243 13.3335 11.119 13.3335 10.8711V6.33334M1.3335 5.66667L7.76165 2.45259C7.8491 2.40887 7.89283 2.387 7.9387 2.3784C7.97932 2.37078 8.02101 2.37078 8.06163 2.3784C8.1075 2.387 8.15122 2.40887 8.23868 2.45259L14.6668 5.66667L8.23868 8.88075C8.15122 8.92447 8.1075 8.94634 8.06163 8.95494C8.02101 8.96256 7.97932 8.96256 7.9387 8.95494C7.89283 8.94634 7.8491 8.92447 7.76165 8.88075L1.3335 5.66667Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,15 @@
<svg width="252" height="120" viewBox="0 0 252 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="6" width="252" height="8" rx="3" fill="#EAECF0"/>
<rect y="26" width="252" height="8" rx="3" fill="#EAECF0"/>
<rect y="46" width="252" height="8" rx="3" fill="#EAECF0"/>
<rect y="66" width="252" height="8" rx="3" fill="#EAECF0"/>
<rect y="86" width="252" height="8" rx="3" fill="#EAECF0"/>
<g clip-path="url(#clip0_1562_6752)">
<rect y="106" width="256" height="8" rx="3" fill="#EAECF0"/>
</g>
<defs>
<clipPath id="clip0_1562_6752">
<rect width="252" height="20" fill="white" transform="translate(0 100)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 643 B

View File

@@ -0,0 +1,23 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3434_4797)">
<path d="M2.75 4.38325C2.75 3.40316 2.75 2.91311 2.94074 2.53877C3.10852 2.20948 3.37623 1.94177 3.70552 1.77399C4.07986 1.58325 4.56991 1.58325 5.55 1.58325H9.16667L13.25 5.66659V11.6166C13.25 12.5967 13.25 13.0867 13.0593 13.4611C12.8915 13.7904 12.6238 14.0581 12.2945 14.2258C11.9201 14.4166 11.4301 14.4166 10.45 14.4166H5.55C4.56991 14.4166 4.07986 14.4166 3.70552 14.2258C3.37623 14.0581 3.10852 13.7904 2.94074 13.4611C2.75 13.0867 2.75 12.5967 2.75 11.6166V4.38325Z" fill="#EC5B27"/>
</g>
<g opacity="0.96">
<path d="M5.49319 11.5001V9.00806H4.88069V9.96706H4.22969V9.00806H3.61719V11.5001H4.22969V10.5131H4.88069V11.5001H5.49319Z" fill="white"/>
<path d="M7.68659 9.55406V9.00806H5.84909V9.55406H6.46159V11.5001H7.07409V9.55406H7.68659Z" fill="white"/>
<path d="M10.3675 11.5001V9.00806H9.76546L9.20546 10.1071L8.64546 9.00806H8.04346V11.5001H8.65596V10.3066L9.00946 10.9226H9.40146L9.75496 10.3066V11.5001H10.3675Z" fill="white"/>
<path d="M12.5291 11.5001V10.9541H11.4826V9.00806H10.8701V11.5001H12.5291Z" fill="white"/>
</g>
<path opacity="0.5" d="M9.1665 1.58325L13.2498 5.66658H10.3332C9.68884 5.66658 9.1665 5.14425 9.1665 4.49992V1.58325Z" fill="white"/>
<defs>
<filter id="filter0_d_3434_4797" x="0.75" y="0.583252" width="14.5" height="16.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3434_4797"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3434_4797" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,23 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3434_2726)">
<path d="M2.75 4.38325C2.75 3.40316 2.75 2.91311 2.94074 2.53877C3.10852 2.20948 3.37623 1.94177 3.70552 1.77399C4.07986 1.58325 4.56991 1.58325 5.55 1.58325H9.16667L13.25 5.66659V11.6166C13.25 12.5967 13.25 13.0867 13.0593 13.4611C12.8915 13.7904 12.6238 14.0581 12.2945 14.2258C11.9201 14.4166 11.4301 14.4166 10.45 14.4166H5.55C4.56991 14.4166 4.07986 14.4166 3.70552 14.2258C3.37623 14.0581 3.10852 13.7904 2.94074 13.4611C2.75 13.0867 2.75 12.5967 2.75 11.6166V4.38325Z" fill="#2D2D2E"/>
</g>
<g opacity="0.96">
<path d="M5.3045 10.6461V9.00806H4.692V10.6321C4.692 10.8701 4.5555 10.9751 4.3665 10.9751C4.23 10.9751 4.167 10.9261 4.09 10.8491L3.6875 11.2481C3.8905 11.4511 4.076 11.5211 4.3665 11.5211C4.8355 11.5211 5.3045 11.2376 5.3045 10.6461Z" fill="white"/>
<path d="M7.44519 10.7371C7.44519 10.5061 7.39269 10.3206 7.25969 10.1911C7.15469 10.0861 6.99369 10.0161 6.76969 9.98456L6.46869 9.94256C6.38119 9.93206 6.32869 9.90056 6.29719 9.86906C6.25869 9.83056 6.24819 9.78506 6.24819 9.75356C6.24819 9.64506 6.33569 9.52256 6.54919 9.52256C6.65769 9.52256 6.86419 9.51206 7.02169 9.66956L7.40669 9.28456C7.19319 9.07106 6.92369 8.98706 6.56669 8.98706C5.99969 8.98706 5.65669 9.31956 5.65669 9.77456C5.65669 9.98806 5.71269 10.1561 5.82819 10.2751C5.94019 10.3906 6.10819 10.4641 6.32869 10.4956L6.62969 10.5376C6.71019 10.5481 6.76269 10.5726 6.79419 10.6041C6.82919 10.6426 6.84319 10.6916 6.84319 10.7511C6.84319 10.8946 6.72769 10.9751 6.48619 10.9751C6.28669 10.9751 6.05919 10.9296 5.92969 10.8001L5.53769 11.1921C5.78969 11.4511 6.10119 11.5211 6.48269 11.5211C7.00769 11.5211 7.44519 11.2446 7.44519 10.7371Z" fill="white"/>
<path d="M9.66339 10.2541C9.66339 9.87956 9.67389 9.52956 9.41139 9.26706C9.22939 9.08506 9.01939 8.98706 8.71489 8.98706C8.41039 8.98706 8.20039 9.08506 8.01839 9.26706C7.75589 9.52956 7.76639 9.87956 7.76639 10.2541C7.76639 10.6286 7.75589 10.9786 8.01839 11.2411C8.20039 11.4231 8.41039 11.5211 8.71489 11.5211C9.01939 11.5211 9.22939 11.4231 9.41139 11.2411C9.67389 10.9786 9.66339 10.6286 9.66339 10.2541ZM9.05089 10.2541C9.05089 10.7091 9.01589 10.7896 8.95989 10.8631C8.91439 10.9226 8.82689 10.9751 8.71489 10.9751C8.60289 10.9751 8.51539 10.9226 8.46989 10.8631C8.41389 10.7896 8.37889 10.7091 8.37889 10.2541C8.37889 9.79906 8.41389 9.71506 8.46989 9.64156C8.51539 9.58206 8.60289 9.53306 8.71489 9.53306C8.82689 9.53306 8.91439 9.58206 8.95989 9.64156C9.01589 9.71506 9.05089 9.79906 9.05089 10.2541Z" fill="white"/>
<path d="M12.0317 11.5001V9.00806H11.4192V10.2611L10.6212 9.00806H10.0857V11.5001H10.6982V10.2471L11.4962 11.5001H12.0317Z" fill="white"/>
</g>
<path opacity="0.5" d="M9.1665 1.58325L13.2498 5.66658H10.3332C9.68884 5.66658 9.1665 5.14425 9.1665 4.49992V1.58325Z" fill="white"/>
<defs>
<filter id="filter0_d_3434_2726" x="0.75" y="0.583252" width="14.5" height="16.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3434_2726"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3434_2726" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.12" d="M10 2H10.8C11.9201 2 12.4802 2 12.908 2.21799C13.2843 2.40973 13.5903 2.71569 13.782 3.09202C14 3.51984 14 4.0799 14 5.2V10.8C14 11.9201 14 12.4802 13.782 12.908C13.5903 13.2843 13.2843 13.5903 12.908 13.782C12.4802 14 11.9201 14 10.8 14H10V2Z" fill="#344054"/>
<path d="M10 2V14M5.2 2H10.8C11.9201 2 12.4802 2 12.908 2.21799C13.2843 2.40973 13.5903 2.71569 13.782 3.09202C14 3.51984 14 4.0799 14 5.2V10.8C14 11.9201 14 12.4802 13.782 12.908C13.5903 13.2843 13.2843 13.5903 12.908 13.782C12.4802 14 11.9201 14 10.8 14H5.2C4.07989 14 3.51984 14 3.09202 13.782C2.71569 13.5903 2.40973 13.2843 2.21799 12.908C2 12.4802 2 11.9201 2 10.8V5.2C2 4.07989 2 3.51984 2.21799 3.09202C2.40973 2.71569 2.71569 2.40973 3.09202 2.21799C3.51984 2 4.0799 2 5.2 2Z" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 960 B

View File

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.12" d="M10 2H10.8C11.9201 2 12.4802 2 12.908 2.21799C13.2843 2.40973 13.5903 2.71569 13.782 3.09202C14 3.51984 14 4.0799 14 5.2V10.8C14 11.9201 14 12.4802 13.782 12.908C13.5903 13.2843 13.2843 13.5903 12.908 13.782C12.4802 14 11.9201 14 10.8 14H10V2Z" fill="#004EEB"/>
<path d="M10 2V14M5.2 2H10.8C11.9201 2 12.4802 2 12.908 2.21799C13.2843 2.40973 13.5903 2.71569 13.782 3.09202C14 3.51984 14 4.0799 14 5.2V10.8C14 11.9201 14 12.4802 13.782 12.908C13.5903 13.2843 13.2843 13.5903 12.908 13.782C12.4802 14 11.9201 14 10.8 14H5.2C4.07989 14 3.51984 14 3.09202 13.782C2.71569 13.5903 2.40973 13.2843 2.21799 12.908C2 12.4802 2 11.9201 2 10.8V5.2C2 4.07989 2 3.51984 2.21799 3.09202C2.40973 2.71569 2.71569 2.40973 3.09202 2.21799C3.51984 2 4.0799 2 5.2 2Z" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 960 B

View File

@@ -0,0 +1,18 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3434_2745)">
<path d="M2.75 4.38325C2.75 3.40316 2.75 2.91311 2.94074 2.53877C3.10852 2.20949 3.37623 1.94177 3.70552 1.77399C4.07986 1.58325 4.56991 1.58325 5.55 1.58325H9.16667L13.25 5.66659V11.6166C13.25 12.5967 13.25 13.0867 13.0593 13.4611C12.8915 13.7904 12.6238 14.0581 12.2945 14.2258C11.9201 14.4166 11.4301 14.4166 10.45 14.4166H5.55C4.56991 14.4166 4.07986 14.4166 3.70552 14.2258C3.37623 14.0581 3.10852 13.7904 2.94074 13.4611C2.75 13.0867 2.75 12.5967 2.75 11.6166V4.38325Z" fill="#309BEC"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6208 12.0833H5.37921C5.13526 12.0833 4.9375 11.8996 4.9375 11.6731V8.99341C4.9375 8.76689 5.13526 8.58325 5.37921 8.58325H10.6208C10.8647 8.58325 11.0625 8.76689 11.0625 8.99341V11.6731C11.0625 11.8996 10.8647 12.0833 10.6208 12.0833ZM6.40986 11.2629V10.1965L6.9988 10.8801L7.58774 10.1965V11.2629H8.17668V9.40356H7.58774L6.9988 10.0872L6.40986 9.40356H5.82091V11.2629H6.40986ZM9.76683 10.3333H10.3558L9.47236 11.2903L8.58894 10.3333H9.17788V9.40356H9.76683V10.3333Z" fill="white"/>
<path opacity="0.5" d="M9.1665 1.58325L13.2498 5.66658H10.3332C9.68884 5.66658 9.1665 5.14425 9.1665 4.49992V1.58325Z" fill="white"/>
<defs>
<filter id="filter0_d_3434_2745" x="0.75" y="0.583252" width="14.5" height="16.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3434_2745"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3434_2745" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.33333 6.33333H8M5.33333 8.66667H10M8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 8.7981 2.15582 9.5598 2.43871 10.2563C2.49285 10.3897 2.51992 10.4563 2.532 10.5102C2.54381 10.5629 2.54813 10.6019 2.54814 10.6559C2.54814 10.7111 2.53812 10.7713 2.51807 10.8916L2.12275 13.2635C2.08135 13.5119 2.06065 13.6361 2.09917 13.7259C2.13289 13.8045 2.19552 13.8671 2.27412 13.9008C2.36393 13.9393 2.48812 13.9186 2.73651 13.8772L5.10843 13.4819C5.22872 13.4619 5.28887 13.4519 5.34409 13.4519C5.3981 13.4519 5.43711 13.4562 5.48981 13.468C5.54369 13.4801 5.61035 13.5072 5.74366 13.5613C6.4402 13.8442 7.2019 14 8 14Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 849 B

View File

@@ -0,0 +1,4 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.75 4.5C6.7165 4.5 7.5 3.7165 7.5 2.75C7.5 1.7835 6.7165 1 5.75 1C4.7835 1 4 1.7835 4 2.75C4 3.7165 4.7835 4.5 5.75 4.5Z" fill="#444CE7"/>
<path d="M3.48775 4.314C3.36842 4.14172 3.30875 4.05558 3.24448 4.02712C3.18679 4.00157 3.12605 3.99844 3.06603 4.01794C2.99918 4.03965 2.94661 4.10099 2.84146 4.22367C2.41951 4.71598 2.13172 5.32705 2.03543 6.00009H2C1.72386 6.00009 1.5 5.77623 1.5 5.50009C1.5 5.31565 1.59961 5.15388 1.75036 5.06668C1.98939 4.9284 2.07107 4.62254 1.9328 4.38351C1.79453 4.14448 1.48867 4.0628 1.24964 4.20107C0.802591 4.45967 0.5 4.94425 0.5 5.50009C0.5 6.32852 1.17157 7.00009 2 7.00009H2.03545C2.14342 7.75422 2.49192 8.43113 2.99997 8.94961L2.99997 10.1117C2.99994 10.1712 2.99992 10.2424 3.00504 10.305C3.01097 10.3776 3.02619 10.4816 3.08171 10.5906C3.15362 10.7317 3.26835 10.8465 3.40948 10.9184C3.51845 10.9739 3.62245 10.9891 3.69505 10.9951C3.7577 11.0002 3.82881 11.0001 3.88836 11.0001H4.86154C4.92109 11.0001 4.99224 11.0002 5.05488 10.9951C5.12749 10.9891 5.23149 10.9739 5.34046 10.9184C5.48158 10.8465 5.59632 10.7317 5.66822 10.5906C5.72375 10.4816 5.73897 10.3776 5.7449 10.305C5.75002 10.2424 5.75 10.1712 5.74997 10.1117L5.74997 10.0001H6.24998L6.24997 10.1115C6.24995 10.1711 6.24992 10.2422 6.25504 10.3048C6.26097 10.3775 6.2762 10.4815 6.33172 10.5904C6.40363 10.7315 6.51836 10.8463 6.65948 10.9182C6.76846 10.9737 6.87245 10.9889 6.94506 10.9949C7.0077 11 7.0788 11 7.13835 10.9999H8.11159C8.17113 11 8.24229 11 8.30493 10.9949C8.37753 10.9889 8.48153 10.9737 8.5905 10.9182C8.73162 10.8463 8.84636 10.7315 8.91827 10.5904C8.97379 10.4815 8.98901 10.3775 8.99494 10.3048C9.00006 10.2422 9.00004 10.1711 9.00001 10.1115L9.00001 9.66299C9.55312 9.40029 10.0258 8.99721 10.3726 8.49993L10.6116 8.49994C10.6711 8.49996 10.7423 8.49999 10.8049 8.49487C10.8775 8.48893 10.9815 8.47371 11.0905 8.41819C11.2316 8.34628 11.3464 8.23155 11.4183 8.09043C11.4738 7.98145 11.489 7.87746 11.4949 7.80485C11.5001 7.74221 11.5 7.67109 11.5 7.61154V5.88181C11.5 5.82509 11.5001 5.75735 11.4954 5.69761C11.49 5.62851 11.4763 5.5294 11.4257 5.42448C11.352 5.27143 11.2285 5.14794 11.0755 5.07422C10.9705 5.02369 10.8714 5.00992 10.8023 5.00454C10.7577 5.00106 10.7087 5.0002 10.6631 4.99999C10.4953 4.64662 10.2702 4.32616 10 4.05044L10 3.51615C10 3.43874 10.0001 3.35111 9.99335 3.27574C9.98593 3.19252 9.96656 3.06385 9.88754 2.93633C9.78902 2.77733 9.63465 2.66089 9.4547 2.60984C9.31038 2.56889 9.18134 2.58561 9.09929 2.60134C9.02497 2.61559 8.94073 2.63969 8.8663 2.66098L8.78839 2.68324C8.6859 2.71252 8.63465 2.72716 8.59861 2.75356C8.5638 2.77904 8.54252 2.80415 8.52309 2.84266C8.50297 2.88255 8.49603 2.94339 8.48215 3.06506C8.32585 4.43546 7.16224 5.5 5.75 5.5C4.81225 5.5 3.98413 5.03063 3.48775 4.314Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,22 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3434_9549)">
<path d="M2.75 4.38325C2.75 3.40316 2.75 2.91311 2.94074 2.53877C3.10852 2.20948 3.37623 1.94177 3.70552 1.77399C4.07986 1.58325 4.56991 1.58325 5.55 1.58325H9.16667L13.25 5.66659V11.6166C13.25 12.5967 13.25 13.0867 13.0593 13.4611C12.8915 13.7904 12.6238 14.0581 12.2945 14.2258C11.9201 14.4166 11.4301 14.4166 10.45 14.4166H5.55C4.56991 14.4166 4.07986 14.4166 3.70552 14.2258C3.37623 14.0581 3.10852 13.7904 2.94074 13.4611C2.75 13.0867 2.75 12.5967 2.75 11.6166V4.38325Z" fill="#DD3633"/>
</g>
<g opacity="0.96">
<path d="M6.81016 9.80956C6.81016 9.40006 6.51266 9.00806 5.95966 9.00806H4.97266V11.5001H5.58516V10.6111H5.95966C6.51266 10.6111 6.81016 10.2191 6.81016 9.80956ZM6.19766 9.80956C6.19766 9.93906 6.09966 10.0616 5.93166 10.0616H5.58516V9.55756H5.93166C6.09966 9.55756 6.19766 9.68006 6.19766 9.80956Z" fill="white"/>
<path d="M9.02199 10.2541C9.02199 9.86206 9.05349 9.54356 8.76299 9.25306C8.59149 9.08156 8.34999 9.00806 8.09099 9.00806H7.16699V11.5001H8.09099C8.34999 11.5001 8.59149 11.4266 8.76299 11.2551C9.05349 10.9646 9.02199 10.6461 9.02199 10.2541ZM8.40949 10.2541C8.40949 10.6776 8.39199 10.7441 8.34299 10.8141C8.28699 10.8981 8.20299 10.9541 8.04899 10.9541H7.77949V9.55406H8.04899C8.20299 9.55406 8.28699 9.61006 8.34299 9.69406C8.39199 9.76406 8.40949 9.83406 8.40949 10.2541Z" fill="white"/>
<path d="M11.1408 9.55406V9.00806H9.44678V11.5001H10.0593V10.5376H10.9833V9.99156H10.0593V9.55406H11.1408Z" fill="white"/>
</g>
<path opacity="0.5" d="M9.1665 1.58325L13.2498 5.66658H10.3332C9.68884 5.66658 9.1665 5.14425 9.1665 4.49992V1.58325Z" fill="white"/>
<defs>
<filter id="filter0_d_3434_9549" x="0.75" y="0.583252" width="14.5" height="16.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3434_9549"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3434_9549" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,11 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 0.5C6.27614 0.5 6.5 0.723858 6.5 1V2C6.5 2.27614 6.27614 2.5 6 2.5C5.72386 2.5 5.5 2.27614 5.5 2V1C5.5 0.723858 5.72386 0.5 6 0.5Z" fill="#FB6514"/>
<path d="M2.81791 2.11092C2.62265 1.91566 2.30606 1.91566 2.1108 2.11092C1.91554 2.30619 1.91554 2.62277 2.1108 2.81803L2.81791 3.52514C3.01317 3.7204 3.32975 3.7204 3.52502 3.52514C3.72028 3.32988 3.72028 3.01329 3.52502 2.81803L2.81791 2.11092Z" fill="#FB6514"/>
<path d="M0.5 6C0.5 5.72386 0.723858 5.5 1 5.5H2C2.27614 5.5 2.5 5.72386 2.5 6C2.5 6.27614 2.27614 6.5 2 6.5H1C0.723858 6.5 0.5 6.27614 0.5 6Z" fill="#FB6514"/>
<path d="M10 5.5C9.72386 5.5 9.5 5.72386 9.5 6C9.5 6.27614 9.72386 6.5 10 6.5H11C11.2761 6.5 11.5 6.27614 11.5 6C11.5 5.72386 11.2761 5.5 11 5.5H10Z" fill="#FB6514"/>
<path d="M9.18192 8.47482C8.98666 8.27955 8.67008 8.27955 8.47482 8.47482C8.27955 8.67008 8.27955 8.98666 8.47482 9.18192L9.18192 9.88903C9.37718 10.0843 9.69377 10.0843 9.88903 9.88903C10.0843 9.69377 10.0843 9.37718 9.88903 9.18192L9.18192 8.47482Z" fill="#FB6514"/>
<path d="M9.88903 2.81803C10.0843 2.62277 10.0843 2.30619 9.88903 2.11092C9.69377 1.91566 9.37718 1.91566 9.18192 2.11092L8.47482 2.81803C8.27955 3.01329 8.27955 3.32988 8.47482 3.52514C8.67008 3.7204 8.98666 3.7204 9.18192 3.52514L9.88903 2.81803Z" fill="#FB6514"/>
<path d="M6 9.5C6.27614 9.5 6.5 9.72386 6.5 10V11C6.5 11.2761 6.27614 11.5 6 11.5C5.72386 11.5 5.5 11.2761 5.5 11V10C5.5 9.72386 5.72386 9.5 6 9.5Z" fill="#FB6514"/>
<path d="M3.52502 9.18192C3.72028 8.98666 3.72028 8.67008 3.52502 8.47482C3.32975 8.27955 3.01317 8.27955 2.81791 8.47482L2.1108 9.18192C1.91554 9.37718 1.91554 9.69377 2.1108 9.88903C2.30606 10.0843 2.62265 10.0843 2.81791 9.88903L3.52502 9.18192Z" fill="#FB6514"/>
<path d="M6.44837 3.27869C6.36413 3.10804 6.19032 3 6.00001 3C5.8097 3 5.6359 3.10804 5.55166 3.27869L4.89538 4.60823L3.4277 4.82276C3.23942 4.85028 3.08308 4.98228 3.02439 5.16328C2.9657 5.34429 3.01484 5.54291 3.15115 5.67568L4.21275 6.70968L3.96221 8.17048C3.93004 8.35807 4.00716 8.54766 4.16115 8.65953C4.31514 8.77139 4.51928 8.78613 4.68774 8.69754L6.00001 8.00742L7.31229 8.69754C7.48075 8.78613 7.68489 8.77139 7.83888 8.65953C7.99287 8.54766 8.06999 8.35807 8.03782 8.17048L7.78728 6.70968L8.84888 5.67568C8.98519 5.54291 9.03433 5.34429 8.97564 5.16328C8.91695 4.98228 8.76061 4.85028 8.57233 4.82276L7.10465 4.60823L6.44837 3.27869Z" fill="#FB6514"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,10 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1745_6117)">
<path d="M7.99998 4V2.5L9.49998 1L9.99998 2L11 2.5L9.49998 4H7.99998ZM7.99998 4L5.99999 5.99997M11 6C11 8.76142 8.76142 11 6 11C3.23858 11 1 8.76142 1 6C1 3.23858 3.23858 1 6 1M8.5 6C8.5 7.38071 7.38071 8.5 6 8.5C4.61929 8.5 3.5 7.38071 3.5 6C3.5 4.61929 4.61929 3.5 6 3.5" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_1745_6117">
<rect width="12" height="12" fill="white" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 635 B

View File

@@ -0,0 +1,23 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_3434_4808)">
<path d="M2.75 4.38325C2.75 3.40316 2.75 2.91311 2.94074 2.53877C3.10852 2.20948 3.37623 1.94177 3.70552 1.77399C4.07986 1.58325 4.56991 1.58325 5.55 1.58325H9.16667L13.25 5.66659V11.6166C13.25 12.5967 13.25 13.0867 13.0593 13.4611C12.8915 13.7904 12.6238 14.0581 12.2945 14.2258C11.9201 14.4166 11.4301 14.4166 10.45 14.4166H5.55C4.56991 14.4166 4.07986 14.4166 3.70552 14.2258C3.37623 14.0581 3.10852 13.7904 2.94074 13.4611C2.75 13.0867 2.75 12.5967 2.75 11.6166V4.38325Z" fill="#E3E5E8"/>
<path d="M3 4.38325C3 3.88908 3.00019 3.53349 3.02301 3.25421C3.04559 2.97786 3.08907 2.79832 3.16349 2.65227C3.3073 2.37002 3.53677 2.14055 3.81901 1.99674C3.96507 1.92232 4.14461 1.87884 4.42096 1.85626C4.70024 1.83345 5.05583 1.83325 5.55 1.83325H9.06311L13 5.77014V11.6166C13 12.1108 12.9998 12.4664 12.977 12.7456C12.9544 13.022 12.9109 13.2015 12.8365 13.3476C12.6927 13.6298 12.4632 13.8593 12.181 14.0031C12.0349 14.0775 11.8554 14.121 11.579 14.1436C11.2998 14.1664 10.9442 14.1666 10.45 14.1666H5.55C5.05583 14.1666 4.70024 14.1664 4.42096 14.1436C4.14461 14.121 3.96507 14.0775 3.81901 14.0031C3.53677 13.8593 3.3073 13.6298 3.16349 13.3476C3.08907 13.2015 3.04559 13.022 3.02301 12.7456C3.00019 12.4664 3 12.1108 3 11.6166V4.38325Z" stroke="black" stroke-opacity="0.03" stroke-width="0.5"/>
</g>
<g opacity="0.96">
<path d="M6.78623 9.55406V9.00806H4.94873V9.55406H5.56123V11.5001H6.17373V9.55406H6.78623Z" fill="#667085"/>
<path d="M9.11009 11.5001L8.33309 10.2226L9.06109 9.00806H8.36459L8.00059 9.71156L7.63659 9.00806H6.94009L7.66809 10.2226L6.89109 11.5001H7.59109L8.00059 10.7336L8.41009 11.5001H9.11009Z" fill="#667085"/>
<path d="M11.0519 9.55406V9.00806H9.21436V9.55406H9.82686V11.5001H10.4394V9.55406H11.0519Z" fill="#667085"/>
</g>
<path opacity="0.5" d="M9.1665 1.58325L13.2498 5.66658H10.3332C9.68884 5.66658 9.1665 5.14425 9.1665 4.49992V1.58325Z" fill="white"/>
<defs>
<filter id="filter0_d_3434_4808" x="0.75" y="0.583252" width="14.5" height="16.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3434_4808"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3434_4808" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 3.5H8M6 3.5V8.5M3.9 10.5H8.1C8.94008 10.5 9.36012 10.5 9.68099 10.3365C9.96323 10.1927 10.1927 9.96323 10.3365 9.68099C10.5 9.36012 10.5 8.94008 10.5 8.1V3.9C10.5 3.05992 10.5 2.63988 10.3365 2.31901C10.1927 2.03677 9.96323 1.8073 9.68099 1.66349C9.36012 1.5 8.94008 1.5 8.1 1.5H3.9C3.05992 1.5 2.63988 1.5 2.31901 1.66349C2.03677 1.8073 1.8073 2.03677 1.66349 2.31901C1.5 2.63988 1.5 3.05992 1.5 3.9V8.1C1.5 8.94008 1.5 9.36012 1.66349 9.68099C1.8073 9.96323 2.03677 10.1927 2.31901 10.3365C2.63988 10.5 3.05992 10.5 3.9 10.5Z" stroke="#667085" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 717 B

View File

@@ -0,0 +1,87 @@
import React, { FC, CSSProperties } from "react";
import { FixedSizeList as List } from "react-window";
import InfiniteLoader from "react-window-infinite-loader";
import type { SegmentDetailModel } from "@/models/datasets";
import SegmentCard from "./SegmentCard";
import s from "./style.module.css";
type IInfiniteVirtualListProps = {
hasNextPage?: boolean; // Are there more items to load? (This information comes from the most recent API request.)
isNextPageLoading: boolean; // Are we currently loading a page of items? (This may be an in-flight flag in your Redux store for example.)
items: Array<SegmentDetailModel[]>; // Array of items loaded so far.
loadNextPage: () => Promise<any>; // Callback function responsible for loading the next page of items.
onClick: (detail: SegmentDetailModel) => void;
onChangeSwitch: (segId: string, enabled: boolean) => Promise<void>;
};
const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
hasNextPage,
isNextPageLoading,
items,
loadNextPage,
onClick: onClickCard,
onChangeSwitch,
}) => {
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? items.length + 1 : items.length;
// Only load 1 page of items at a time.
// Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
const loadMoreItems = isNextPageLoading ? () => { } : loadNextPage;
// Every row is loaded except for our loading indicator row.
const isItemLoaded = (index: number) => !hasNextPage || index < items.length;
// Render an item or a loading indicator.
const Item = ({ index, style }: { index: number; style: CSSProperties }) => {
let content;
if (!isItemLoaded(index)) {
content = (
<>
{[1, 2, 3].map((v) => (
<SegmentCard loading={true} detail={{ position: v } as any} />
))}
</>
);
} else {
content = items[index].map((segItem) => (
<SegmentCard
key={segItem.id}
detail={segItem}
onClick={() => onClickCard(segItem)}
onChangeSwitch={onChangeSwitch}
loading={false}
/>
));
}
return (
<div style={style} className={s.cardWrapper}>
{content}
</div>
);
};
return (
<InfiniteLoader
itemCount={itemCount}
isItemLoaded={isItemLoaded}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
ref={ref}
className="List"
height={800}
width={"100%"}
itemSize={200}
itemCount={itemCount}
onItemsRendered={onItemsRendered}
>
{Item}
</List>
)}
</InfiniteLoader>
);
};
export default InfiniteVirtualList;

View File

@@ -0,0 +1,166 @@
import type { FC } from "react";
import React from "react";
import cn from "classnames";
import { ArrowUpRightIcon } from "@heroicons/react/24/outline";
import Switch from "@/app/components/base/switch";
import Divider from "@/app/components/base/divider";
import Indicator from "@/app/components/header/indicator";
import { formatNumber } from "@/utils/format";
import type { SegmentDetailModel } from "@/models/datasets";
import { StatusItem } from "../../list";
import s from "./style.module.css";
import { SegmentIndexTag } from "./index";
import { DocumentTitle } from '../index'
import { useTranslation } from "react-i18next";
const ProgressBar: FC<{ percent: number; loading: boolean }> = ({ percent, loading }) => {
return (
<div className={s.progressWrapper}>
<div className={cn(s.progress, loading ? s.progressLoading : '')}>
<div
className={s.progressInner}
style={{ width: `${loading ? 0 : (percent * 100).toFixed(2)}%` }}
/>
</div>
<div className={loading ? s.progressTextLoading : s.progressText}>{loading ? null : percent.toFixed(2)}</div>
</div>
)
}
export type UsageScene = 'doc' | 'hitTesting'
type ISegmentCardProps = {
loading: boolean;
detail?: SegmentDetailModel & { document: { name: string } };
score?: number
onClick?: () => void;
onChangeSwitch?: (segId: string, enabled: boolean) => Promise<void>;
scene?: UsageScene
className?: string;
};
const SegmentCard: FC<ISegmentCardProps> = ({
detail = {},
score,
onClick,
onChangeSwitch,
loading = true,
scene = 'doc',
className = ''
}) => {
const { t } = useTranslation()
const {
id,
position,
enabled,
content,
word_count,
hit_count,
index_node_hash,
} = detail as any;
const isDocScene = scene === 'doc'
return (
<div
className={cn(
s.segWrapper,
isDocScene && !enabled ? "bg-gray-25" : "",
"group",
!loading ? "pb-4" : "",
className,
)}
onClick={() => onClick?.()}
>
<div className={s.segTitleWrapper}>
{isDocScene ? <>
<SegmentIndexTag positionId={position} className={cn("w-fit group-hover:opacity-100", isDocScene && !enabled ? 'opacity-50' : '')} />
<div className={s.segStatusWrapper}>
{loading ? (
<Indicator
color="gray"
className="bg-gray-200 border-gray-300 shadow-none"
/>
) : (
<>
<StatusItem status={enabled ? "enabled" : "disabled"} reverse textCls="text-gray-500 text-xs" />
<div className="hidden group-hover:inline-flex items-center">
<Divider type="vertical" className="!h-2" />
<div
onClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) =>
e.stopPropagation()
}
className="inline-flex items-center"
>
<Switch
size='md'
defaultValue={enabled}
onChange={async (val) => {
await onChangeSwitch?.(id, val)
}}
/>
</div>
</div>
</>
)}
</div>
</> : <div className={s.hitTitleWrapper}>
<div className={cn(s.commonIcon, s.targetIcon, loading ? '!bg-gray-300' : '', '!w-3.5 !h-3.5')} />
<ProgressBar percent={score ?? 0} loading={loading} />
</div>}
</div>
{loading ? (
<div className={cn(s.cardLoadingWrapper, s.cardLoadingIcon)}>
<div className={cn(s.cardLoadingBg)} />
</div>
) : (
isDocScene ? <>
<div
className={cn(
s.segContent,
enabled ? "" : "opacity-50",
"group-hover:text-transparent group-hover:bg-clip-text group-hover:bg-gradient-to-b"
)}
>
{content}
</div>
<div className={cn('group-hover:flex', s.segData)}>
<div className="flex items-center mr-6">
<div className={cn(s.commonIcon, s.typeSquareIcon)}></div>
<div className={s.segDataText}>{formatNumber(word_count)}</div>
</div>
<div className="flex items-center mr-6">
<div className={cn(s.commonIcon, s.targetIcon)} />
<div className={s.segDataText}>{formatNumber(hit_count)}</div>
</div>
<div className="flex items-center">
<div className={cn(s.commonIcon, s.bezierCurveIcon)} />
<div className={s.segDataText}>{index_node_hash}</div>
</div>
</div>
</> : <>
<div className="h-[140px] overflow-hidden text-ellipsis text-sm font-normal text-gray-800">
{content}
</div>
<div className={cn("w-full bg-gray-50 group-hover:bg-white")}>
<Divider />
<div className="relative flex items-center w-full">
<DocumentTitle
name={detail?.document?.name || ''}
extension={(detail?.document?.name || '').split('.').pop() || 'txt'}
wrapperCls='w-full'
iconCls="!h-4 !w-4 !bg-contain"
textCls="text-xs text-gray-700 !font-normal overflow-hidden whitespace-nowrap text-ellipsis"
/>
<div className={cn(s.chartLinkText, 'group-hover:inline-flex')}>
{t('datasetHitTesting.viewChart')}
<ArrowUpRightIcon className="w-3 h-3 ml-1 stroke-current stroke-2" />
</div>
</div>
</div>
</>
)}
</div>
);
};
export default SegmentCard;

View File

@@ -0,0 +1,206 @@
'use client'
import type { FC } from 'react'
import React, { memo, useState, useEffect, useMemo } from 'react'
import { HashtagIcon } from '@heroicons/react/24/solid'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { omitBy, isNil, debounce } from 'lodash-es'
import { formatNumber } from '@/utils/format'
import { StatusItem } from '../../list'
import { DocumentContext } from '../index'
import s from './style.module.css'
import Modal from '@/app/components/base/modal'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
import { ToastContext } from '@/app/components/base/toast'
import { SimpleSelect, Item } from '@/app/components/base/select'
import { disableSegment, enableSegment, fetchSegments } from '@/service/datasets'
import type { SegmentDetailModel, SegmentsResponse, SegmentsQuery } from '@/models/datasets'
import { asyncRunSafe } from '@/utils'
import type { CommonResponse } from '@/models/common'
import InfiniteVirtualList from "./InfiniteVirtualList";
import cn from 'classnames'
export const SegmentIndexTag: FC<{ positionId: string | number; className?: string }> = ({ positionId, className }) => {
const localPositionId = useMemo(() => {
const positionIdStr = String(positionId)
if (positionIdStr.length >= 3)
return positionId
return positionIdStr.padStart(3, '0')
}, [positionId])
return (
<div className={`text-gray-500 border border-gray-200 box-border flex items-center rounded-md italic text-[11px] pl-1 pr-1.5 font-medium ${className ?? ''}`}>
<HashtagIcon className='w-3 h-3 text-gray-400 fill-current mr-1 stroke-current stroke-1' />
{localPositionId}
</div>
)
}
type ISegmentDetailProps = {
segInfo?: Partial<SegmentDetailModel> & { id: string }
onChangeSwitch?: (segId: string, enabled: boolean) => Promise<void>
}
/**
* Show all the contents of the segment
*/
export const SegmentDetail: FC<ISegmentDetailProps> = memo(({
segInfo,
onChangeSwitch }) => {
const { t } = useTranslation()
return (
<div className={'flex flex-col'}>
<SegmentIndexTag positionId={segInfo?.position || ''} className='w-fit mb-6' />
<div className={s.segModalContent}>{segInfo?.content}</div>
<div className={s.keywordTitle}>{t('datasetDocuments.segment.keywords')}</div>
<div className={s.keywordWrapper}>
{!segInfo?.keywords?.length
? '-'
: segInfo?.keywords?.map((word: any) => {
return <div className={s.keyword}>{word}</div>
})}
</div>
<div className={cn(s.footer, s.numberInfo)}>
<div className='flex items-center'>
<div className={cn(s.commonIcon, s.typeSquareIcon)} /><span className='mr-8'>{formatNumber(segInfo?.word_count as any)} {t('datasetDocuments.segment.characters')}</span>
<div className={cn(s.commonIcon, s.targetIcon)} /><span className='mr-8'>{formatNumber(segInfo?.hit_count as any)} {t('datasetDocuments.segment.hitCount')}</span>
<div className={cn(s.commonIcon, s.bezierCurveIcon)} /><span className={s.hashText}>{t('datasetDocuments.segment.vectorHash')}{segInfo?.index_node_hash}</span>
</div>
<div className='flex items-center'>
<StatusItem status={segInfo?.enabled ? 'enabled' : 'disabled'} reverse textCls='text-gray-500 text-xs' />
<Divider type='vertical' className='!h-2' />
<Switch
size='md'
defaultValue={segInfo?.enabled}
onChange={async val => {
await onChangeSwitch?.(segInfo?.id || '', val)
}}
/>
</div>
</div>
</div>
)
})
export const splitArray = (arr: any[], size = 3) => {
if (!arr || !arr.length)
return []
const result = []
for (let i = 0; i < arr.length; i += size)
result.push(arr.slice(i, i + size))
return result
}
type ICompletedProps = {
// data: Array<{}> // all/part segments
}
/**
* Embedding done, show list of all segments
* Support search and filter
*/
const Completed: FC<ICompletedProps> = () => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { datasetId = '', documentId = '' } = useContext(DocumentContext)
// the current segment id and whether to show the modal
const [currSegment, setCurrSegment] = useState<{ segInfo?: SegmentDetailModel; showModal: boolean }>({ showModal: false })
const [searchValue, setSearchValue] = useState() // the search value
const [selectedStatus, setSelectedStatus] = useState<boolean | 'all'>('all') // the selected status, enabled/disabled/undefined
const [lastSegmentsRes, setLastSegmentsRes] = useState<SegmentsResponse | undefined>(undefined)
const [allSegments, setAllSegments] = useState<Array<SegmentDetailModel[]>>([]) // all segments data
const [loading, setLoading] = useState(false)
const [total, setTotal] = useState<number | undefined>()
useEffect(() => {
if (lastSegmentsRes !== undefined) {
getSegments(false)
}
}, [selectedStatus, searchValue])
const onChangeStatus = ({ value }: Item) => {
setSelectedStatus(value === 'all' ? 'all' : !!value)
}
const getSegments = async (needLastId?: boolean) => {
const finalLastId = lastSegmentsRes?.data?.[lastSegmentsRes.data.length - 1]?.id || '';
setLoading(true)
const [e, res] = await asyncRunSafe<SegmentsResponse>(fetchSegments({
datasetId,
documentId,
params: omitBy({
last_id: !needLastId ? undefined : finalLastId,
limit: 9,
keyword: searchValue,
enabled: selectedStatus === 'all' ? 'all' : !!selectedStatus,
}, isNil) as SegmentsQuery
}) as Promise<SegmentsResponse>)
if (!e) {
setAllSegments([...(!needLastId ? [] : allSegments), ...splitArray(res.data || [])])
setLastSegmentsRes(res)
if (!lastSegmentsRes) { setTotal(res?.total || 0) }
}
setLoading(false)
}
const onClickCard = (detail: SegmentDetailModel) => {
setCurrSegment({ segInfo: detail, showModal: true })
}
const onCloseModal = () => {
setCurrSegment({ ...currSegment, showModal: false })
}
const onChangeSwitch = async (segId: string, enabled: boolean) => {
const opApi = enabled ? enableSegment : disableSegment
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, segmentId: segId }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
for (const item of allSegments) {
for (const seg of item) {
if (seg.id === segId) {
seg.enabled = enabled
}
}
}
setAllSegments([...allSegments])
} else {
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
}
}
return (
<>
<div className={s.docSearchWrapper}>
<div className={s.totalText}>{total ? formatNumber(total) : '--'} {t('datasetDocuments.segment.paragraphs')}</div>
<SimpleSelect
onSelect={onChangeStatus}
items={[
{ value: 'all', name: t('datasetDocuments.list.index.all') },
{ value: 0, name: t('datasetDocuments.list.status.disabled') },
{ value: 1, name: t('datasetDocuments.list.status.enabled') },
]}
defaultValue={'all'}
className={s.select}
wrapperClassName='h-fit w-[120px] mr-2' />
<Input showPrefix wrapperClassName='!w-52' className='!h-8' onChange={debounce(setSearchValue, 500)} />
</div>
<InfiniteVirtualList
hasNextPage={lastSegmentsRes?.has_more ?? true}
isNextPageLoading={loading}
items={allSegments}
loadNextPage={getSegments}
onChangeSwitch={onChangeSwitch}
onClick={onClickCard}
/>
<Modal isShow={currSegment.showModal} onClose={onCloseModal} className='!max-w-[640px]' closable>
<SegmentDetail segInfo={currSegment.segInfo ?? { id: '' }} onChangeSwitch={onChangeSwitch} />
</Modal>
</>
)
}
export default Completed

View File

@@ -0,0 +1,130 @@
/* .cardWrapper {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(290px, auto));
grid-gap: 16px;
grid-auto-rows: 180px;
} */
.totalText {
@apply text-gray-900 font-medium text-base flex-1;
}
.docSearchWrapper {
@apply sticky w-full h-10 -top-3 bg-white flex items-center mb-3 justify-between z-10;
}
.listContainer {
height: calc(100% - 3.25rem);
@apply box-border pb-[30px];
}
.cardWrapper {
@apply grid gap-4 grid-cols-3 min-w-[902px] last:mb-[30px];
}
.segWrapper {
@apply box-border h-[180px] min-w-[290px] bg-gray-50 px-4 pt-4 flex flex-col text-opacity-50 rounded-xl border border-transparent hover:border-gray-200 hover:shadow-lg hover:cursor-pointer hover:bg-white;
}
.segTitleWrapper {
@apply flex items-center justify-between;
}
.segStatusWrapper {
@apply flex items-center box-border;
}
.segContent {
white-space: wrap;
@apply flex-1 h-0 min-h-0 mt-2 text-sm text-gray-800 overflow-ellipsis overflow-hidden from-gray-800 to-white;
}
.segData {
@apply hidden text-gray-500 text-xs pt-2;
}
.segDataText {
@apply max-w-[80px] truncate;
}
.chartLinkText {
background: linear-gradient(to left, white, 90%, transparent);
@apply text-primary-600 font-semibold text-xs absolute right-0 hidden h-12 pl-12 items-center;
}
.select {
@apply h-8 py-0 bg-gray-50 hover:bg-gray-100 rounded-lg shadow-none !important;
}
.segModalContent {
@apply h-96 text-gray-800 text-base overflow-y-scroll;
}
.footer {
@apply flex items-center justify-between box-border border-t-gray-200 border-t-[0.5px] pt-3 mt-4;
}
.numberInfo {
@apply text-gray-500 text-xs font-medium;
}
.keywordTitle {
@apply text-gray-500 mb-2 mt-1 text-xs uppercase;
}
.keywordWrapper {
@apply text-gray-700 w-full max-h-[200px] overflow-auto flex flex-wrap;
}
.keyword {
@apply text-sm border border-gray-200 max-w-[200px] max-h-[100px] whitespace-pre-line overflow-y-auto mr-1 mb-2 last:mr-0 px-2 py-1 rounded-lg;
}
.hashText {
@apply w-48 inline-block truncate;
}
.commonIcon {
@apply w-3 h-3 inline-block align-middle mr-1 bg-gray-500;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center center;
}
.targetIcon {
mask-image: url(../../assets/target.svg);
}
.typeSquareIcon {
mask-image: url(../../assets/typeSquare.svg);
}
.bezierCurveIcon {
mask-image: url(../../assets/bezierCurve.svg);
}
.cardLoadingWrapper {
@apply relative w-full h-full inline-block rounded-b-xl;
background-position: center center;
background-repeat: no-repeat;
background-size: 100% 100%;
background-origin: content-box;
}
.cardLoadingIcon {
background-image: url(../../assets/cardLoading.svg);
}
/* .hitLoadingIcon {
background-image: url(../../assets/hitLoading.svg);
} */
.cardLoadingBg {
@apply h-full relative rounded-b-xl mt-4;
left: calc(-1rem - 1px);
width: calc(100% + 2rem + 2px);
height: calc(100% - 1rem + 1px);
background: linear-gradient(
180deg,
rgba(252, 252, 253, 0) 0%,
#fcfcfd 74.15%
);
}
.hitTitleWrapper {
@apply w-full flex items-center justify-between mb-2;
}
.progressWrapper {
@apply flex items-center justify-between w-full;
}
.progress {
border-radius: 3px;
@apply relative h-1.5 box-border border border-gray-300 flex-1 mr-2;
}
.progressLoading {
@apply border-[#EAECF0] bg-[#EAECF0];
}
.progressInner {
@apply absolute top-0 h-full bg-gray-300;
}
.progressText {
font-size: 13px;
@apply text-gray-700 font-bold;
}
.progressTextLoading {
border-radius: 5px;
@apply h-3.5 w-3.5 bg-[#EAECF0];
}

View File

@@ -0,0 +1,268 @@
import { FC, useCallback, useMemo, useState } from 'react'
import React from 'react'
import useSWR from 'swr'
import { useRouter } from 'next/navigation'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { omit } from 'lodash-es'
import cn from 'classnames'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import { ToastContext } from '@/app/components/base/toast'
import { FullDocumentDetail, ProcessRuleResponse } from '@/models/datasets'
import type { CommonResponse } from '@/models/common'
import { asyncRunSafe } from '@/utils'
import { formatNumber } from '@/utils/format'
import { fetchProcessRule, fetchIndexingEstimate, fetchIndexingStatus, pauseDocIndexing, resumeDocIndexing } from '@/service/datasets'
import SegmentCard from '../completed/SegmentCard'
import { FieldInfo } from '../metadata'
import s from './style.module.css'
import style from '../completed/style.module.css'
import { DocumentContext } from '../index'
import DatasetDetailContext from '@/context/dataset-detail'
import StopEmbeddingModal from '@/app/components/datasets/create/stop-embedding-modal'
import { ArrowRightIcon } from '@heroicons/react/24/solid'
type Props = {
detail?: FullDocumentDetail
stopPosition?: 'top' | 'bottom'
datasetId?: string
documentId?: string
indexingType?: string
}
const StopIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<g clip-path="url(#clip0_2328_2798)">
<path d="M1.5 3.9C1.5 3.05992 1.5 2.63988 1.66349 2.31901C1.8073 2.03677 2.03677 1.8073 2.31901 1.66349C2.63988 1.5 3.05992 1.5 3.9 1.5H8.1C8.94008 1.5 9.36012 1.5 9.68099 1.66349C9.96323 1.8073 10.1927 2.03677 10.3365 2.31901C10.5 2.63988 10.5 3.05992 10.5 3.9V8.1C10.5 8.94008 10.5 9.36012 10.3365 9.68099C10.1927 9.96323 9.96323 10.1927 9.68099 10.3365C9.36012 10.5 8.94008 10.5 8.1 10.5H3.9C3.05992 10.5 2.63988 10.5 2.31901 10.3365C2.03677 10.1927 1.8073 9.96323 1.66349 9.68099C1.5 9.36012 1.5 8.94008 1.5 8.1V3.9Z" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_2328_2798">
<rect width="12" height="12" fill="white" />
</clipPath>
</defs>
</svg>
}
const ResumeIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M10 3.5H5C3.34315 3.5 2 4.84315 2 6.5C2 8.15685 3.34315 9.5 5 9.5H10M10 3.5L8 1.5M10 3.5L8 5.5" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
const RuleDetail: FC<{ sourceData?: ProcessRuleResponse; docName?: string }> = ({ sourceData, docName }) => {
const { t } = useTranslation()
const segmentationRuleMap = {
docName: t('datasetDocuments.embedding.docName'),
mode: t('datasetDocuments.embedding.mode'),
segmentLength: t('datasetDocuments.embedding.segmentLength'),
textCleaning: t('datasetDocuments.embedding.textCleaning'),
}
const getValue = useCallback((field: string) => {
let value: string | number | undefined = '-';
switch (field) {
case 'docName':
value = docName
break;
case 'mode':
value = sourceData?.mode === 'automatic' ? (t('datasetDocuments.embedding.automatic') as string) : (t('datasetDocuments.embedding.custom') as string);
break;
case 'segmentLength':
value = sourceData?.rules?.segmentation?.max_tokens
break;
default:
value = sourceData?.mode === 'automatic' ?
(t('datasetDocuments.embedding.automatic') as string) :
sourceData?.rules?.pre_processing_rules?.map(rule => {
if (rule.enabled) {
return getRuleName(rule.id)
}
}).filter(Boolean).join(';')
break;
}
return value
}, [sourceData, docName])
const getRuleName = (key: string) => {
if (key === 'remove_extra_spaces') {
return t('datasetCreation.stepTwo.removeExtraSpaces')
}
if (key === 'remove_urls_emails') {
return t('datasetCreation.stepTwo.removeUrlEmails')
}
if (key === 'remove_stopwords') {
return t('datasetCreation.stepTwo.removeStopwords')
}
}
return <div className='flex flex-col pt-8 pb-10 first:mt-0'>
{Object.keys(segmentationRuleMap).map((field) => {
return <FieldInfo
key={field}
label={segmentationRuleMap[field as keyof typeof segmentationRuleMap]}
displayedValue={String(getValue(field))}
/>
})}
</div>
}
const EmbeddingDetail: FC<Props> = ({ detail, stopPosition = 'top', datasetId: dstId, documentId: docId, indexingType }) => {
const onTop = stopPosition === 'top'
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { datasetId = '', documentId = '' } = useContext(DocumentContext)
const { indexingTechnique } = useContext(DatasetDetailContext)
const localDatasetId = dstId ?? datasetId
const localDocumentId = docId ?? documentId
const localIndexingTechnique = indexingType ?? indexingTechnique
const { data: indexingStatusDetail, error: indexingStatusErr, mutate: statusMutate } = useSWR({
action: 'fetchIndexingStatus',
datasetId: localDatasetId,
documentId: localDocumentId,
}, apiParams => fetchIndexingStatus(omit(apiParams, 'action')), {
refreshInterval: 5000,
revalidateOnFocus: false,
})
const { data: indexingEstimateDetail, error: indexingEstimateErr } = useSWR({
action: 'fetchIndexingEstimate',
datasetId: localDatasetId,
documentId: localDocumentId,
}, apiParams => fetchIndexingEstimate(omit(apiParams, 'action')), {
revalidateOnFocus: false
})
const { data: ruleDetail, error: ruleError } = useSWR({
action: 'fetchProcessRule',
params: { documentId: localDocumentId }
}, apiParams => fetchProcessRule(omit(apiParams, 'action')), {
revalidateOnFocus: false,
})
const [showModal, setShowModal] = useState(false)
const modalShowHandle = () => setShowModal(true)
const modalCloseHandle = () => setShowModal(false)
const router = useRouter()
const navToDocument = () => {
router.push(`/datasets/${localDatasetId}/documents/${localDocumentId}`)
}
const isEmbedding = useMemo(() => ['indexing', 'splitting', 'parsing', 'cleaning'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail])
const isEmbeddingCompleted = useMemo(() => ['completed'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail])
const isEmbeddingPaused = useMemo(() => ['paused'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail])
const isEmbeddingError = useMemo(() => ['error'].includes(indexingStatusDetail?.indexing_status || ''), [indexingStatusDetail])
const percent = useMemo(() => {
const completedCount = indexingStatusDetail?.completed_segments || 0
const totalCount = indexingStatusDetail?.total_segments || 0
if (totalCount === 0) return 0
const percent = Math.round(completedCount * 100 / totalCount)
return percent > 100 ? 100 : percent
}, [indexingStatusDetail])
const handleSwitch = async () => {
const opApi = isEmbedding ? pauseDocIndexing : resumeDocIndexing
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId: localDatasetId, documentId: localDocumentId }) as Promise<CommonResponse>)
if (!e) {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
statusMutate()
} else {
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
}
}
// if (!ruleDetail && !error)
// return <Loading type='app' />
return (
<>
<div className={s.embeddingStatus}>
{isEmbedding && t('datasetDocuments.embedding.processing')}
{isEmbeddingCompleted && t('datasetDocuments.embedding.completed')}
{isEmbeddingPaused && t('datasetDocuments.embedding.paused')}
{isEmbeddingError && t('datasetDocuments.embedding.error')}
{onTop && isEmbedding && (
<Button onClick={handleSwitch} className={s.opBtn}>
<StopIcon className={s.opIcon} />
{t('datasetDocuments.embedding.stop')}
</Button>
)}
{onTop && isEmbeddingPaused && (
<Button onClick={handleSwitch} className={s.opBtn}>
<ResumeIcon className={s.opIcon} />
{t('datasetDocuments.embedding.resume')}
</Button>
)}
</div>
{/* progress bar */}
<div className={s.progressContainer}>
{new Array(10).fill('').map((_, idx) => <div
key={idx}
className={cn(s.progressBgItem, isEmbedding ? 'bg-primary-50' : 'bg-gray-100')}
/>)}
<div className={
cn('rounded-l-md',
s.progressBar,
(isEmbedding || isEmbeddingCompleted) && s.barProcessing,
(isEmbeddingPaused || isEmbeddingError) && s.barPaused,
indexingStatusDetail?.indexing_status === 'completed' && 'rounded-r-md')
}
style={{ width: `${percent}%` }}
/>
</div>
<div className={s.progressData}>
<div>{t('datasetDocuments.embedding.segments')} {indexingStatusDetail?.completed_segments}/{indexingStatusDetail?.total_segments} · {percent}%</div>
{localIndexingTechnique === 'high_quaility' && (
<div className='flex items-center'>
<div className={cn(s.commonIcon, s.highIcon)} />
{t('datasetDocuments.embedding.highQuality')} · {t('datasetDocuments.embedding.estimate')}
<span className={s.tokens}>{formatNumber(indexingEstimateDetail?.tokens || 0)}</span>tokens
(<span className={s.price}>${formatNumber(indexingEstimateDetail?.total_price || 0)}</span>)
</div>
)}
{localIndexingTechnique === 'economy' && (
<div className='flex items-center'>
<div className={cn(s.commonIcon, s.economyIcon)} />
{t('datasetDocuments.embedding.economy')} · {t('datasetDocuments.embedding.estimate')}
<span className={s.tokens}>0</span>tokens
</div>
)}
</div>
<RuleDetail sourceData={ruleDetail} docName={detail?.name} />
{!onTop && (
<div className='flex items-center gap-2 mt-10'>
{isEmbedding && (
<Button onClick={modalShowHandle} className='w-fit'>
{t('datasetCreation.stepThree.stop')}
</Button>
)}
{isEmbeddingPaused && (
<Button onClick={handleSwitch} className='w-fit'>
{t('datasetCreation.stepThree.resume')}
</Button>
)}
<Button className='w-fit' type='primary' onClick={navToDocument}>
<span>{t('datasetCreation.stepThree.navTo')}</span>
<ArrowRightIcon className='h-4 w-4 ml-2 stroke-current stroke-1' />
</Button>
</div>
)}
{onTop && <>
<Divider />
<div className={s.previewTip}>{t('datasetDocuments.embedding.previewTip')}</div>
<div className={style.cardWrapper}>
{[1, 2, 3].map((v) => (
<SegmentCard loading={true} detail={{ position: v } as any} />
))}
</div>
</>}
<StopEmbeddingModal show={showModal} onConfirm={handleSwitch} onHide={modalCloseHandle} />
</>
)
}
export default EmbeddingDetail

View File

@@ -0,0 +1,59 @@
.progressBar {
@apply absolute top-0 h-4;
}
.barPaused {
background: linear-gradient(
270deg,
rgba(208, 213, 221, 0.8) -2.21%,
rgba(208, 213, 221, 0.5) 100%
);
}
.barProcessing {
background: linear-gradient(
90deg,
rgba(41, 112, 255, 0.9) 0%,
rgba(21, 94, 239, 0.9) 100%
);
}
.opBtn {
@apply w-fit h-6 text-xs px-2 py-1 text-gray-700 rounded-md !important;
}
.opIcon {
@apply mr-1 stroke-current text-gray-700 w-3 h-3;
}
.progressContainer {
@apply relative flex mb-2 h-4 rounded-md w-full;
}
.progressBgItem {
@apply flex-1 border-r border-r-white first:rounded-l-md;
}
.progressBgItem:nth-last-child(2) {
@apply rounded-r-md;
}
.progressData {
@apply w-full flex justify-between items-center text-xs text-gray-700;
}
.previewTip {
@apply pb-1 pt-12 text-gray-900 text-sm font-medium;
}
.embeddingStatus {
@apply flex items-center justify-between text-gray-900 font-medium text-base mb-3;
}
.commonIcon {
@apply w-3 h-3 mr-1 inline-block align-middle;
}
.highIcon {
mask-image: url(../../assets/star.svg);
@apply bg-orange-500;
}
.economyIcon {
background-color: #444ce7;
mask-image: url(../../assets/normal.svg);
}
.tokens {
@apply text-xs font-medium px-1;
}
.price {
color: #f79009;
@apply text-xs font-medium;
}

View File

@@ -0,0 +1,121 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import useSWR from 'swr'
import { ArrowLeftIcon } from '@heroicons/react/24/solid'
import { createContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { omit } from 'lodash-es'
import cn from 'classnames'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import { fetchDocumentDetail, MetadataType } from '@/service/datasets'
import { OperationAction, StatusItem } from '../list'
import Completed from './completed'
import Embedding from './embedding'
import Metadata from './metadata'
import s from '../style.module.css'
import style from './style.module.css'
export const BackCircleBtn: FC<{ onClick: () => void }> = ({ onClick }) => {
return (
<div onClick={onClick} className={'rounded-full w-8 h-8 flex justify-center items-center border-gray-100 cursor-pointer border hover:border-gray-300 shadow-[0px_12px_16px_-4px_rgba(16,24,40,0.08),0px_4px_6px_-2px_rgba(16,24,40,0.03)]'}>
<ArrowLeftIcon className='text-primary-600 fill-current stroke-current h-4 w-4' />
</div>
)
}
export const DocumentContext = createContext<{ datasetId?: string; documentId?: string }>({})
type DocumentTitleProps = {
extension?: string;
name?: string;
iconCls?: string;
textCls?: string;
wrapperCls?: string;
}
export const DocumentTitle: FC<DocumentTitleProps> = ({ extension, name, iconCls, textCls, wrapperCls }) => {
const localExtension = extension?.toLowerCase() || name?.split('.')?.pop()?.toLowerCase()
return <div className={cn('flex items-center justify-start flex-1', wrapperCls)}>
<div className={cn(s[`${localExtension || 'txt'}Icon`], style.titleIcon, iconCls)}></div>
<span className={cn('font-semibold text-lg text-gray-900 ml-1', textCls)}> {name || '--'}</span>
</div>
}
type Props = {
datasetId: string
documentId: string
}
const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
const { t } = useTranslation()
const router = useRouter()
const [showMetadata, setShowMetadata] = useState(true)
const { data: documentDetail, error, mutate: detailMutate } = useSWR({
action: 'fetchDocumentDetail',
datasetId,
documentId,
params: { metadata: 'without' as MetadataType }
}, apiParams => fetchDocumentDetail(omit(apiParams, 'action')))
const { data: documentMetadata, error: metadataErr, mutate: metadataMutate } = useSWR({
action: 'fetchDocumentDetail',
datasetId,
documentId,
params: { metadata: 'only' as MetadataType }
}, apiParams => fetchDocumentDetail(omit(apiParams, 'action')))
const backToPrev = () => {
router.push(`/datasets/${datasetId}/documents`)
}
const isDetailLoading = !documentDetail && !error
const isMetadataLoading = !documentMetadata && !metadataErr
const embedding = ['queuing', 'indexing', 'paused'].includes((documentDetail?.display_status || '').toLowerCase())
return (
<DocumentContext.Provider value={{ datasetId, documentId }}>
<div className='flex flex-col h-full'>
<div className='flex h-16 border-b-gray-100 border-b items-center p-4'>
<BackCircleBtn onClick={backToPrev} />
<Divider className='!h-4' type='vertical' />
<DocumentTitle extension={documentDetail?.data_source_info?.upload_file?.extension} name={documentDetail?.name} />
<StatusItem status={documentDetail?.display_status || 'available'} scene='detail' />
<OperationAction
scene='detail'
detail={{
enabled: documentDetail?.enabled || false,
archived: documentDetail?.archived || false,
id: documentId
}}
datasetId={datasetId}
onUpdate={detailMutate}
className='!w-[216px]'
/>
<button
className={cn(style.layoutRightIcon, showMetadata ? style.iconShow : style.iconClose)}
onClick={() => setShowMetadata(!showMetadata)}
/>
</div>
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
{isDetailLoading ? <Loading type='app' /> :
<div className={`box-border h-full w-full overflow-y-scroll ${embedding ? 'py-12 px-16' : 'pb-[30px] pt-3 px-6'}`}>
{embedding ? <Embedding detail={documentDetail} /> : <Completed />}
</div>
}
{showMetadata && <Metadata
docDetail={{ ...documentDetail, ...documentMetadata } as any}
loading={isMetadataLoading}
onUpdate={metadataMutate}
/>}
</div>
</div>
</DocumentContext.Provider>
)
}
export default DocumentDetail

View File

@@ -0,0 +1,363 @@
'use client'
import type { FC } from 'react'
import React, { useState, useEffect } from 'react'
import { PencilIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { get } from 'lodash-es'
import cn from 'classnames'
import Input from '@/app/components/base/input'
import Button from '@/app/components/base/button'
import Tooltip from '@/app/components/base/tooltip'
import Radio from '@/app/components/base/radio'
import Divider from '@/app/components/base/divider'
import { ToastContext } from '@/app/components/base/toast'
import { SimpleSelect } from '@/app/components/base/select'
import Loading from '@/app/components/base/loading'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
import { asyncRunSafe, getTextWidthWithCanvas } from '@/utils'
import { modifyDocMetadata } from '@/service/datasets'
import type { CommonResponse } from '@/models/common'
import type { FullDocumentDetail, DocType } from '@/models/datasets'
import { CUSTOMIZABLE_DOC_TYPES } from '@/models/datasets'
import type { metadataType, inputType } from '@/hooks/use-metadata'
import { useMetadataMap, useLanguages, useBookCategories, usePersonalDocCategories, useBusinessDocCategories } from '@/hooks/use-metadata'
import { DocumentContext } from '../index'
import s from './style.module.css'
const map2Options = (map: { [key: string]: string }) => {
return Object.keys(map).map(key => ({ value: key, name: map[key] }))
}
type IFieldInfoProps = {
label: string
value?: string
displayedValue?: string
defaultValue?: string
showEdit?: boolean
inputType?: inputType
selectOptions?: Array<{ value: string; name: string }>
onUpdate?: (v: any) => void
}
export const FieldInfo: FC<IFieldInfoProps> = ({
label,
value = '',
displayedValue = '',
defaultValue,
showEdit = false,
inputType = 'input',
selectOptions = [],
onUpdate
}) => {
const { t } = useTranslation()
const textNeedWrap = getTextWidthWithCanvas(displayedValue) > 190
const editAlignTop = showEdit && inputType === 'textarea'
const readAlignTop = !showEdit && textNeedWrap
return (
<div className={cn(s.fieldInfo, editAlignTop && `!items-start`, readAlignTop && '!items-start pt-1')}>
<div className={cn(s.label, editAlignTop && 'pt-1')}>{label}</div>
<div className={s.value}>
{!showEdit
? displayedValue
: inputType === 'select'
? <SimpleSelect
onSelect={({ value }) => onUpdate && onUpdate(value as string)}
items={selectOptions}
defaultValue={value}
className={s.select}
wrapperClassName={s.selectWrapper}
placeholder={`${t('datasetDocuments.metadata.placeholder.select')}${label}`}
/>
: inputType === 'textarea'
? <AutoHeightTextarea
onChange={e => onUpdate && onUpdate(e.target.value)}
value={value}
className={s.textArea}
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
/>
: <Input
className={s.input}
onChange={onUpdate}
value={value}
defaultValue={defaultValue}
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
/>
}
</div>
</div>
)
}
const TypeIcon: FC<{ iconName: string; className?: string }> = ({ iconName, className = '' }) => {
return <div className={cn(s.commonIcon, s[`${iconName}Icon`], className)}
/>
}
const IconButton: FC<{
type: DocType
isChecked: boolean
}> = ({ type, isChecked = false }) => {
const metadataMap = useMetadataMap()
return (
<Tooltip content={metadataMap[type].text} selector={`doc-metadata-${type}`}>
<button className={cn(s.iconWrapper, 'group', isChecked ? s.iconCheck : '')}>
<TypeIcon
iconName={metadataMap[type].iconName || ''}
className={`group-hover:bg-primary-600 ${isChecked ? '!bg-primary-600' : ''}`}
/>
</button>
</Tooltip>
)
}
type IMetadataProps = {
docDetail?: FullDocumentDetail
loading: boolean
onUpdate: () => void
}
const Metadata: FC<IMetadataProps> = ({ docDetail, loading, onUpdate }) => {
const { doc_metadata = {} } = docDetail || {}
const doc_type = docDetail?.doc_type || ''
const { t } = useTranslation()
const metadataMap = useMetadataMap()
const languageMap = useLanguages()
const bookCategoryMap = useBookCategories()
const personalDocCategoryMap = usePersonalDocCategories()
const businessDocCategoryMap = useBusinessDocCategories()
const [editStatus, setEditStatus] = useState(!doc_type) // if no documentType, in editing status by default
// the initial values are according to the documentType
const [metadataParams, setMetadataParams] = useState<{
documentType?: DocType | '';
metadata: { [key: string]: string }
}>(
doc_type ? {
documentType: doc_type,
metadata: doc_metadata || {}
} : { metadata: {} })
const [showDocTypes, setShowDocTypes] = useState(!doc_type) // whether show doc types
const [tempDocType, setTempDocType] = useState<DocType | undefined | ''>('') // for remember icon click
const [saveLoading, setSaveLoading] = useState(false)
const { notify } = useContext(ToastContext)
const { datasetId = '', documentId = '' } = useContext(DocumentContext)
useEffect(() => {
if (docDetail?.doc_type) {
setEditStatus(false)
setShowDocTypes(false)
setTempDocType(docDetail?.doc_type)
setMetadataParams({
documentType: docDetail?.doc_type,
metadata: docDetail?.doc_metadata || {}
})
}
}, [docDetail?.doc_type])
// confirm doc type
const confirmDocType = () => {
if (!tempDocType) return;
setMetadataParams({
documentType: tempDocType,
metadata: tempDocType === metadataParams.documentType ? metadataParams.metadata : {} // change doc type, clear metadata
})
setEditStatus(true)
setShowDocTypes(false)
}
// cancel doc type
const cancelDocType = () => {
setTempDocType(metadataParams.documentType)
setEditStatus(true)
setShowDocTypes(false)
}
// show doc type select
const renderSelectDocType = () => {
const { documentType } = metadataParams
return (
<>
{!doc_type && !documentType && <>
<div className={s.desc}>{t('datasetDocuments.metadata.desc')}</div>
</>}
<div className={s.operationWrapper}>
{!doc_type && !documentType && <>
<span className={s.title}>{t('datasetDocuments.metadata.docTypeSelectTitle')}</span>
</>}
{documentType && <>
<span className={s.title}>{t('datasetDocuments.metadata.docTypeChangeTitle')}</span>
<span className={s.changeTip}>{t('datasetDocuments.metadata.docTypeSelectWarning')}</span>
</>}
<Radio.Group value={tempDocType ?? documentType} onChange={setTempDocType} className={s.radioGroup}>
{CUSTOMIZABLE_DOC_TYPES.map((type) => {
const currValue = tempDocType ?? documentType
return <Radio value={type} className={`${s.radio} ${currValue === type ? 'shadow-none' : ''}`}>
<IconButton
type={type}
isChecked={currValue === type}
/>
</Radio>
})}
</Radio.Group>
{!doc_type && !documentType && (
<Button type='primary'
onClick={confirmDocType}
disabled={!tempDocType}
>
{t('datasetDocuments.metadata.firstMetaAction')}
</Button>
)}
{documentType && <div className={s.opBtnWrapper}>
<Button onClick={confirmDocType} className={`${s.opBtn} ${s.opSaveBtn}`} type='primary' >{t('common.operation.save')}</Button>
<Button onClick={cancelDocType} className={`${s.opBtn} ${s.opCancelBtn}`}>{t('common.operation.cancel')}</Button>
</div>}
</div >
</>
)
}
// show metadata info and edit
const renderFieldInfos = ({ mainField = 'book', canEdit }: { mainField?: metadataType | ''; canEdit?: boolean }) => {
if (!mainField) return null
const fieldMap = metadataMap[mainField]?.subFieldsMap
const sourceData = ['originInfo', 'technicalParameters'].includes(mainField) ? docDetail : metadataParams.metadata
const getTargetMap = (field: string) => {
if (field === 'language') return languageMap
if (field === 'category' && mainField === 'book') {
return bookCategoryMap
}
if (field === 'document_type') {
if (mainField === 'personal_document') return personalDocCategoryMap
if (mainField === 'business_document') return businessDocCategoryMap
}
return {} as any
}
const getTargetValue = (field: string) => {
const val = get(sourceData, field, '')
if (!val && val !== 0) return '-'
if (fieldMap[field]?.inputType === 'select')
return getTargetMap(field)[val]
if (fieldMap[field]?.render)
return fieldMap[field]?.render?.(val, field === 'hit_count' ? get(sourceData, 'segment_count', 0) as number : undefined)
return val
}
return <div className='flex flex-col gap-1'>
{Object.keys(fieldMap).map((field) => {
return <FieldInfo
key={fieldMap[field]?.label}
label={fieldMap[field]?.label}
displayedValue={getTargetValue(field)}
value={get(sourceData, field, '')}
inputType={fieldMap[field]?.inputType || 'input'}
showEdit={canEdit}
onUpdate={(val) => {
setMetadataParams(pre => ({ ...pre, metadata: { ...pre.metadata, [field]: val } }))
}}
selectOptions={map2Options(getTargetMap(field))}
/>
})}
</div>
}
const enabledEdit = () => {
setEditStatus(true)
}
const onCancel = () => {
setMetadataParams({ documentType: doc_type || '', metadata: { ...(docDetail?.doc_metadata || {}) } })
setEditStatus(!doc_type)
if (!doc_type)
setShowDocTypes(true)
}
const onSave = async () => {
console.log('metadataParams:', metadataParams)
setSaveLoading(true)
const [e] = await asyncRunSafe<CommonResponse>(modifyDocMetadata({
datasetId,
documentId,
body: {
doc_type: metadataParams.documentType || doc_type || '',
doc_metadata: metadataParams.metadata
}
}) as Promise<CommonResponse>)
if (!e)
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
else
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
onUpdate?.()
setEditStatus(false)
setSaveLoading(false)
}
return (
<div className={`${s.main} ${editStatus ? 'bg-white' : 'bg-gray-25'}`}>
{loading ? <Loading type='app' /> : (
<>
<div className={s.titleWrapper}>
<span className={s.title}>{t('datasetDocuments.metadata.title')}</span>
{!editStatus
? <Button onClick={enabledEdit} className={`${s.opBtn} ${s.opEditBtn}`}>
<PencilIcon className={s.opIcon} />
{t('common.operation.edit')}
</Button>
: showDocTypes
? null
: <div className={s.opBtnWrapper}>
<Button onClick={onCancel} className={`${s.opBtn} ${s.opCancelBtn}`}>{t('common.operation.cancel')}</Button>
<Button onClick={onSave}
className={`${s.opBtn} ${s.opSaveBtn}`}
type='primary'
loading={saveLoading}
>
{t('common.operation.save')}
</Button>
</div>}
</div>
{/* show selected doc type and changing entry */}
{!editStatus
? <div className={s.documentTypeShow}>
<TypeIcon iconName={metadataMap[doc_type || 'book']?.iconName || ''} className={s.iconShow} />
{metadataMap[doc_type || 'book'].text}
</div>
: showDocTypes
? null
: <div className={s.documentTypeShow}>
{metadataParams.documentType && <>
<TypeIcon iconName={metadataMap[metadataParams.documentType || 'book'].iconName || ''} className={s.iconShow} />
{metadataMap[metadataParams.documentType || 'book'].text}
{editStatus && <div className='inline-flex items-center gap-1 ml-1'>
·
<div
onClick={() => { setShowDocTypes(true) }}
className='cursor-pointer hover:text-[#155EEF]'
>
{t('common.operation.change')}
</div>
</div>}
</>}
</div>
}
{(!doc_type && showDocTypes) ? null : <Divider />}
{showDocTypes ? renderSelectDocType() : renderFieldInfos({ mainField: metadataParams.documentType, canEdit: editStatus })}
{/* show fixed fields */}
<Divider />
{renderFieldInfos({ mainField: 'originInfo', canEdit: false })}
<div className={`${s.title} mt-8`}>{metadataMap.technicalParameters.text}</div>
<Divider />
{renderFieldInfos({ mainField: 'technicalParameters', canEdit: false })}
</>
)}
</div>
)
}
export default Metadata

View File

@@ -0,0 +1,114 @@
.main {
@apply w-96 xl:w-[360px] flex-shrink-0 px-6 py-5 overflow-y-auto border-l-gray-100 border-l;
}
.operationWrapper {
@apply flex flex-col items-center gap-4 mt-7 mb-8;
}
.iconWrapper {
@apply box-border cursor-pointer h-8 w-8 inline-flex items-center justify-center;
@apply border-[#EAECF5] border rounded-lg hover:border-primary-200 hover:bg-primary-25 hover:shadow-[0px_4px_8px_-2px_rgba(16,24,40,0.1),0px_2px_4px_-2px_rgba(16,24,40,0.06)];
}
.icon {
@apply h-4 w-4 stroke-current stroke-[2px] text-gray-700 group-hover:stroke-primary-600;
}
.iconCheck {
@apply border-primary-400 border-[1.5px] bg-primary-25 shadow-[0px_1px_3px_rgba(16,24,40,0.1),0px_1px_2px_rgba(16,24,40,0.06)] !important;
}
.commonIcon {
@apply w-4 h-4 inline-block align-middle bg-gray-700 hover:bg-primary-600;
}
.bookOpenIcon {
mask-image: url(../../assets/bookOpen.svg);
}
.globeIcon {
mask-image: url(../../assets/globe.svg);
}
.graduationHatIcon {
mask-image: url(../../assets/graduationHat.svg);
}
.fileIcon {
mask-image: url(../../assets/file.svg);
}
.briefcaseIcon {
mask-image: url(../../assets/briefcase.svg);
}
.atSignIcon {
mask-image: url(../../assets/atSign.svg);
}
.messageTextCircleIcon {
mask-image: url(../../assets/messageTextCircle.svg);
}
.radioGroup {
@apply !bg-transparent !gap-2;
}
.radio {
@apply !p-0 !mr-0 hover:bg-transparent !rounded-lg;
}
.title {
@apply text-sm text-gray-800 font-medium leading-6;
}
.titleWrapper {
@apply flex items-center justify-between;
}
.desc {
@apply text-gray-500 text-xs;
}
.fieldInfo {
/* height: 1.75rem; */
min-height: 1.75rem;
@apply flex flex-row items-center gap-4;
}
.fieldInfo > .label {
@apply w-2/5 max-w-[128px] text-gray-500 text-xs font-medium overflow-hidden text-ellipsis whitespace-nowrap;
}
.fieldInfo > .value {
overflow-wrap: anywhere;
@apply w-3/5 text-gray-700 font-normal text-xs;
}
.changeTip {
@apply text-[#D92D20] text-xs text-center;
}
.opBtnWrapper {
@apply flex items-center justify-center gap-1;
}
.opBtn {
@apply h-6 w-14 px-0 text-xs font-medium rounded-md !important;
}
.opEditBtn {
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
@apply border-[0.5px] border-gray-200 bg-white !important;
}
.opCancelBtn {
@apply border-none bg-gray-50 font-medium text-gray-700 hover:bg-gray-100 !important;
}
.opSaveBtn {
@apply border-primary-700 border-[0.5px] font-medium hover:border-none !important;
}
.opIcon {
@apply h-3 w-3 stroke-current stroke-2 mr-1;
}
.select {
@apply h-7 py-0 pl-2 text-xs bg-gray-50 hover:bg-gray-100 rounded-md shadow-none !important;
}
.selectWrapper {
@apply !h-7 w-full
}
.selectWrapper ul {
@apply text-xs
}
.selectWrapper li {
@apply flex items-center h-8
}
.documentTypeShow {
@apply flex items-center text-xs text-gray-500;
}
.iconShow {
mask-size: contain;
@apply w-3 h-3 bg-gray-500 hover:bg-none mr-1 !important;
}
.textArea {
@apply placeholder:text-gray-400 bg-gray-50 px-2 py-1 caret-primary-600 rounded-md hover:bg-gray-100 focus-visible:outline-none focus-visible:bg-white focus-visible:border focus-visible:border-gray-300 hover:shadow-[0_1px_2px_rgba(16,24,40,0.05);];
}
.input {
@apply bg-gray-50 hover:bg-gray-100 focus-visible:bg-white !important
}

View File

@@ -0,0 +1,15 @@
.titleIcon {
background-position-x: center;
background-repeat: no-repeat;
background-size: 28px 28px;
@apply h-6 w-6 !important;
}
.layoutRightIcon {
@apply w-8 h-8 ml-2 box-border border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer hover:shadow-[0_1px_2px_rgba(16,24,40,0.05)];
}
.iconShow {
background: center center url(../assets/layoutRightShow.svg) no-repeat;
}
.iconClose {
background: center center url(../assets/layoutRightClose.svg) no-repeat;
}

View File

@@ -0,0 +1,135 @@
'use client'
import type { FC } from 'react'
import React, { useState, useMemo } from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { debounce } from 'lodash-es'
// import Link from 'next/link'
import { PlusIcon } from '@heroicons/react/24/solid'
import { omit } from 'lodash-es'
import List from './list'
import s from './style.module.css'
import Loading from '@/app/components/base/loading'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Pagination from '@/app/components/base/pagination'
import { get } from '@/service/base'
import { fetchDocuments } from '@/service/datasets'
// Custom page count is not currently supported.
const limit = 15
const FolderPlusIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M10.8332 5.83333L9.90355 3.9741C9.63601 3.439 9.50222 3.17144 9.30265 2.97597C9.12615 2.80311 8.91344 2.67164 8.6799 2.59109C8.41581 2.5 8.11668 2.5 7.51841 2.5H4.33317C3.39975 2.5 2.93304 2.5 2.57652 2.68166C2.26292 2.84144 2.00795 3.09641 1.84816 3.41002C1.6665 3.76654 1.6665 4.23325 1.6665 5.16667V5.83333M1.6665 5.83333H14.3332C15.7333 5.83333 16.4334 5.83333 16.9681 6.10582C17.4386 6.3455 17.821 6.72795 18.0607 7.19836C18.3332 7.73314 18.3332 8.4332 18.3332 9.83333V13.5C18.3332 14.9001 18.3332 15.6002 18.0607 16.135C17.821 16.6054 17.4386 16.9878 16.9681 17.2275C16.4334 17.5 15.7333 17.5 14.3332 17.5H5.6665C4.26637 17.5 3.56631 17.5 3.03153 17.2275C2.56112 16.9878 2.17867 16.6054 1.93899 16.135C1.6665 15.6002 1.6665 14.9001 1.6665 13.5V5.83333ZM9.99984 14.1667V9.16667M7.49984 11.6667H12.4998" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
const ThreeDotsIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M5 6.5V5M8.93934 7.56066L10 6.5M10.0103 11.5H11.5103" stroke="#374151" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
const NotionIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<g clip-path="url(#clip0_2164_11263)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5725 18.2611L1.4229 15.5832C0.905706 14.9389 0.625 14.1466 0.625 13.3312V3.63437C0.625 2.4129 1.60224 1.39936 2.86295 1.31328L12.8326 0.632614C13.5569 0.583164 14.2768 0.775682 14.8717 1.17794L18.3745 3.5462C19.0015 3.97012 19.375 4.66312 19.375 5.40266V16.427C19.375 17.6223 18.4141 18.6121 17.1798 18.688L6.11458 19.3692C5.12958 19.4298 4.17749 19.0148 3.5725 18.2611Z" fill="white" />
<path d="M7.03006 8.48669V8.35974C7.03006 8.03794 7.28779 7.77104 7.61997 7.74886L10.0396 7.58733L13.3857 12.5147V8.19009L12.5244 8.07528V8.01498C12.5244 7.68939 12.788 7.42074 13.1244 7.4035L15.326 7.29073V7.60755C15.326 7.75628 15.2154 7.88349 15.0638 7.90913L14.534 7.99874V15.0023L13.8691 15.231C13.3136 15.422 12.6952 15.2175 12.3772 14.7377L9.12879 9.83574V14.5144L10.1287 14.7057L10.1147 14.7985C10.0711 15.089 9.82028 15.3087 9.51687 15.3222L7.03006 15.4329C6.99718 15.1205 7.23132 14.841 7.55431 14.807L7.88143 14.7727V8.53453L7.03006 8.48669Z" fill="black" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9218 1.85424L2.95217 2.53491C2.35499 2.57568 1.89209 3.05578 1.89209 3.63437V13.3312C1.89209 13.8748 2.07923 14.403 2.42402 14.8325L4.57362 17.5104C4.92117 17.9434 5.46812 18.1818 6.03397 18.147L17.0991 17.4658C17.6663 17.4309 18.1078 16.9762 18.1078 16.427V5.40266C18.1078 5.06287 17.9362 4.74447 17.6481 4.54969L14.1453 2.18143C13.7883 1.94008 13.3564 1.82457 12.9218 1.85424ZM3.44654 3.78562C3.30788 3.68296 3.37387 3.46909 3.54806 3.4566L12.9889 2.77944C13.2897 2.75787 13.5886 2.8407 13.8318 3.01305L15.7261 4.35508C15.798 4.40603 15.7642 4.51602 15.6752 4.52086L5.67742 5.0646C5.37485 5.08106 5.0762 4.99217 4.83563 4.81406L3.44654 3.78562ZM5.20848 6.76919C5.20848 6.4444 5.47088 6.1761 5.80642 6.15783L16.3769 5.58216C16.7039 5.56435 16.9792 5.81583 16.9792 6.13239V15.6783C16.9792 16.0025 16.7177 16.2705 16.3829 16.2896L5.8793 16.8872C5.51537 16.9079 5.20848 16.6283 5.20848 16.2759V6.76919Z" fill="black" />
</g>
<defs>
<clipPath id="clip0_2164_11263">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
}
const EmptyElement: FC<{ onClick: () => void; type?: 'upload' | 'sync' }> = ({ onClick, type = 'upload' }) => {
const { t } = useTranslation()
return <div className={s.emptyWrapper}>
<div className={s.emptyElement}>
<div className={s.emptySymbolIconWrapper}>
{type === 'upload' ? <FolderPlusIcon /> : <NotionIcon />}
</div>
<span className={s.emptyTitle}>{t('datasetDocuments.list.empty.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
<div className={s.emptyTip}>
{t(`datasetDocuments.list.empty.${type}.tip`)}
</div>
{type === 'upload' && <Button onClick={onClick} className={s.addFileBtn}>
<PlusIcon className={s.plusIcon} />{t('datasetDocuments.list.addFile')}
</Button>}
</div>
</div>
}
type IDocumentsProps = {
datasetId: string
}
export const fetcher = (url: string) => get(url, {}, { isMock: true })
const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
const { t } = useTranslation()
const [searchValue, setSearchValue] = useState<string>('')
const [currPage, setCurrPage] = React.useState<number>(0)
const router = useRouter()
const query = useMemo(() => {
return { page: currPage + 1, limit, keyword: searchValue }
}, [searchValue, currPage])
const { data: documentsRes, error, mutate } = useSWR({
action: 'fetchDocuments',
datasetId,
params: query,
}, apiParams => fetchDocuments(omit(apiParams, 'action')))
const total = documentsRes?.total || 0
const routeToDocCreate = () => {
router.push(`/datasets/${datasetId}/documents/create`)
}
router.prefetch(`/datasets/${datasetId}/documents/create`)
const isLoading = !documentsRes && !error
return (
<div className='flex flex-col h-full overflow-y-auto'>
<div className='flex flex-col justify-center gap-1 px-6 pt-4'>
<h1 className={s.title}>{t('datasetDocuments.list.title')}</h1>
<p className={s.desc}>{t('datasetDocuments.list.desc')}</p>
</div>
<div className='flex flex-col px-6 py-4 flex-1'>
<div className='flex items-center justify-between'>
<Input
showPrefix
wrapperClassName='!w-[200px]'
className='!h-8 !text-[13px]'
onChange={debounce(setSearchValue, 500)}
value={searchValue}
/>
<Button type='primary' onClick={routeToDocCreate} className='!h-8 !text-[13px]'>
<PlusIcon className='h-4 w-4 mr-2 stroke-current' />
{t('datasetDocuments.list.addFile')}
</Button>
</div>
{isLoading
? <Loading type='app' />
: total > 0
? <List documents={documentsRes?.data || []} datasetId={datasetId} onUpdate={mutate} />
: <EmptyElement onClick={routeToDocCreate} />
}
{/* Show Pagination only if the total is more than the limit */}
{(total && total > limit)
? <Pagination current={currPage} onChange={setCurrPage} total={total} limit={limit} />
: null}
</div>
</div>
)
}
export default Documents

View File

@@ -0,0 +1,318 @@
'use client'
import type { FC } from 'react'
import React, { useState, useEffect } from 'react'
import { TrashIcon, ArrowDownIcon } from '@heroicons/react/24/outline'
import { ExclamationCircleIcon } from '@heroicons/react/24/solid'
import dayjs from 'dayjs'
import { pick } from 'lodash-es'
import { useContext } from 'use-context-selector'
import { useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import Popover from '@/app/components/base/popover'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import Tooltip from '@/app/components/base/tooltip'
import { ToastContext } from '@/app/components/base/toast'
import type { IndicatorProps } from '@/app/components/header/indicator'
import Indicator from '@/app/components/header/indicator'
import { asyncRunSafe } from '@/utils'
import { formatNumber } from '@/utils/format'
import { archiveDocument, deleteDocument, disableDocument, enableDocument } from '@/service/datasets'
import type { DocumentListResponse, DocumentDisplayStatus } from '@/models/datasets'
import type { CommonResponse } from '@/models/common'
import cn from 'classnames'
import s from './style.module.css'
export const SettingsIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M2 5.33325L10 5.33325M10 5.33325C10 6.43782 10.8954 7.33325 12 7.33325C13.1046 7.33325 14 6.43782 14 5.33325C14 4.22868 13.1046 3.33325 12 3.33325C10.8954 3.33325 10 4.22868 10 5.33325ZM6 10.6666L14 10.6666M6 10.6666C6 11.7712 5.10457 12.6666 4 12.6666C2.89543 12.6666 2 11.7712 2 10.6666C2 9.56202 2.89543 8.66659 4 8.66659C5.10457 8.66659 6 9.56202 6 10.6666Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
export const FilePlusIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M13.3332 6.99992V4.53325C13.3332 3.41315 13.3332 2.85309 13.1152 2.42527C12.9234 2.04895 12.6175 1.74299 12.2412 1.55124C11.8133 1.33325 11.2533 1.33325 10.1332 1.33325H5.8665C4.7464 1.33325 4.18635 1.33325 3.75852 1.55124C3.3822 1.74299 3.07624 2.04895 2.88449 2.42527C2.6665 2.85309 2.6665 3.41315 2.6665 4.53325V11.4666C2.6665 12.5867 2.6665 13.1467 2.88449 13.5746C3.07624 13.9509 3.3822 14.2569 3.75852 14.4486C4.18635 14.6666 4.7464 14.6666 5.8665 14.6666H7.99984M9.33317 7.33325H5.33317M6.6665 9.99992H5.33317M10.6665 4.66659H5.33317M11.9998 13.9999V9.99992M9.99984 11.9999H13.9998" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
export const ArchiveIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
<path d="M2.66683 5.33106C2.55749 5.32824 2.47809 5.32191 2.40671 5.30771C1.87779 5.2025 1.46432 4.78904 1.35912 4.26012C1.3335 4.13132 1.3335 3.97644 1.3335 3.66667C1.3335 3.3569 1.3335 3.20201 1.35912 3.07321C1.46432 2.54429 1.87779 2.13083 2.40671 2.02562C2.53551 2 2.69039 2 3.00016 2H13.0002C13.3099 2 13.4648 2 13.5936 2.02562C14.1225 2.13083 14.536 2.54429 14.6412 3.07321C14.6668 3.20201 14.6668 3.3569 14.6668 3.66667C14.6668 3.97644 14.6668 4.13132 14.6412 4.26012C14.536 4.78904 14.1225 5.2025 13.5936 5.30771C13.5222 5.32191 13.4428 5.32824 13.3335 5.33106M6.66683 8.66667H9.3335M2.66683 5.33333H13.3335V10.8C13.3335 11.9201 13.3335 12.4802 13.1155 12.908C12.9238 13.2843 12.6178 13.5903 12.2415 13.782C11.8137 14 11.2536 14 10.1335 14H5.86683C4.74672 14 4.18667 14 3.75885 13.782C3.38252 13.5903 3.07656 13.2843 2.88482 12.908C2.66683 12.4802 2.66683 11.9201 2.66683 10.8V5.33333Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
}
export const useIndexStatus = () => {
const { t } = useTranslation()
return {
queuing: { color: 'orange', text: t('datasetDocuments.list.status.queuing') }, // waiting
indexing: { color: 'blue', text: t('datasetDocuments.list.status.indexing') }, // indexing splitting parsing cleaning
paused: { color: 'orange', text: t('datasetDocuments.list.status.parsed') }, // paused
error: { color: 'red', text: t('datasetDocuments.list.status.error') }, // error
available: { color: 'green', text: t('datasetDocuments.list.status.available') }, // completedarchived = falseenabled = true
enabled: { color: 'green', text: t('datasetDocuments.list.status.enabled') }, // completedarchived = falseenabled = true
disabled: { color: 'gray', text: t('datasetDocuments.list.status.disabled') }, // completedarchived = falseenabled = false
archived: { color: 'gray', text: t('datasetDocuments.list.status.archived') }, // completedarchived = true
}
}
// status item for list
export const StatusItem: FC<{
status: DocumentDisplayStatus;
reverse?: boolean;
scene?: 'list' | 'detail'
textCls?: string
}> = ({ status, reverse = false, scene = 'list', textCls = '' }) => {
const DOC_INDEX_STATUS_MAP = useIndexStatus();
const localStatus = status.toLowerCase() as keyof typeof DOC_INDEX_STATUS_MAP
return <div className={
cn('flex items-center',
reverse ? 'flex-row-reverse' : '',
scene === 'detail' ? s.statusItemDetail : '')
}>
<Indicator color={DOC_INDEX_STATUS_MAP[localStatus]?.color as IndicatorProps['color']} className={reverse ? 'ml-2' : 'mr-2'} />
<span className={cn('text-gray-700 text-sm', textCls)}>{DOC_INDEX_STATUS_MAP[localStatus]?.text}</span>
</div>
}
type OperationName = 'delete' | 'archive' | 'enable' | 'disable'
// operation action for list and detail
export const OperationAction: FC<{
detail: {
enabled: boolean;
archived: boolean;
id: string
}
datasetId: string;
onUpdate: () => void
scene?: 'list' | 'detail'
className?: string
}> = ({ datasetId, detail, onUpdate, scene = 'list', className = '' }) => {
const { id, enabled = false, archived = false } = detail || {}
const [showModal, setShowModal] = useState(false)
const { notify } = useContext(ToastContext)
const { t } = useTranslation()
const isListScene = scene === 'list';
const onOperate = async (operationName: OperationName) => {
let opApi = deleteDocument
switch (operationName) {
case 'archive':
opApi = archiveDocument
break
case 'enable':
opApi = enableDocument
break
case 'disable':
opApi = disableDocument
break
default:
opApi = deleteDocument
break
}
const [e] = await asyncRunSafe<CommonResponse>(opApi({ datasetId, documentId: id }) as Promise<CommonResponse>)
if (!e)
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
else
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
onUpdate()
}
return <div
className='flex items-center'
onClick={(e) => e.stopPropagation()}
>
{isListScene && <>
{archived ?
<Tooltip selector={`list-switch-${id}`} content={t('datasetDocuments.list.action.enableWarning') as string} className='!font-semibold'>
<div>
<Switch defaultValue={false} onChange={() => { }} disabled={true} size='md' />
</div>
</Tooltip> :
<Switch defaultValue={enabled} onChange={v => onOperate(v ? 'enable' : 'disable')} size='md' />
}
<Divider className='!ml-4 !mr-2 !h-3' type='vertical' />
</>}
<Popover
htmlContent={
<div className='w-full py-1'>
{!isListScene && <>
<div className='flex justify-between items-center mx-4 pt-2'>
<span className={cn(s.actionName, 'font-medium')}>
{!archived && enabled ? t('datasetDocuments.list.index.enable') : t('datasetDocuments.list.index.disable')}
</span>
<Tooltip
selector={`detail-switch-${id}`}
content={t('datasetDocuments.list.action.enableWarning') as string}
className='!font-semibold'
disabled={!archived}
>
<div>
<Switch
defaultValue={archived ? false : enabled}
onChange={v => !archived && onOperate(v ? 'enable' : 'disable')}
disabled={archived}
size='md'
/>
</div>
</Tooltip>
</div>
<div className='mx-4 pb-1 pt-0.5 text-xs text-gray-500'>
{!archived && enabled ? t('datasetDocuments.list.index.enableTip') : t('datasetDocuments.list.index.disableTip')}
</div>
<Divider />
</>}
{/* <div className={s.actionItem}>
<SettingsIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span>
</div>
<div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/create`)}>
<FilePlusIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.uploadFile')}</span>
</div>
<Divider className='my-1' /> */}
{!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}>
<ArchiveIcon />
<span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span>
</div>}
<div className={cn(s.actionItem, s.deleteActionItem, 'group')} onClick={() => setShowModal(true)}>
<TrashIcon className={'w-4 h-4 stroke-current text-gray-500 stroke-2 group-hover:text-red-500'} />
<span className={cn(s.actionName, 'group-hover:text-red-500')}>{t('datasetDocuments.list.action.delete')}</span>
</div>
</div>
}
trigger='click'
position='br'
btnElement={<div className={cn(s.actionIcon, s.commonIcon)} />}
btnClassName={(open) => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!bg-gray-100 !shadow-none' : '!bg-transparent')}
className={`!w-[200px] h-fit !z-20 ${className}`}
/>
{showModal && <Modal isShow={showModal} onClose={() => setShowModal(false)} className={s.delModal} closable>
<div>
<div className={s.warningWrapper}>
<ExclamationCircleIcon className={s.warningIcon} />
</div>
<div className='text-xl font-semibold text-gray-900 mb-1'>{t('datasetDocuments.list.delete.title')}</div>
<div className='text-sm text-gray-500 mb-10'>{t('datasetDocuments.list.delete.content')}</div>
<div className='flex gap-2 justify-end'>
<Button onClick={() => setShowModal(false)}>{t('common.operation.cancel')}</Button>
<Button
type='warning'
onClick={() => onOperate('delete')}
className='border-red-700 border-[0.5px]'
>
{t('common.operation.sure')}
</Button>
</div>
</div>
</Modal>}
</div>
}
export const renderTdValue = (value: string | number | null, isEmptyStyle = false) => {
return (
<div className={cn(isEmptyStyle ? 'text-gray-400' : 'text-gray-700', s.tdValue)}>
{value ?? '-'}
</div>
)
}
const renderCount = (count: number | undefined) => {
if (!count) {
return renderTdValue(0, true)
}
if (count < 1000) {
return count;
}
return `${formatNumber((count / 1000).toFixed(1))}k`
}
type IDocumentListProps = {
documents: DocumentListResponse['data']
datasetId: string
onUpdate: () => void
}
/**
* Document list component including basic information
*/
const DocumentList: FC<IDocumentListProps> = ({ documents = [], datasetId, onUpdate }) => {
const { t } = useTranslation()
const router = useRouter()
const [localDocs, setLocalDocs] = useState<DocumentListResponse['data']>(documents);
const [enableSort, setEnableSort] = useState(false);
useEffect(() => {
setLocalDocs(documents)
}, [documents])
const onClickSort = () => {
setEnableSort(!enableSort);
if (!enableSort) {
const sortedDocs = [...localDocs].sort((a, b) => dayjs(a.created_at).isBefore(dayjs(b.created_at)) ? -1 : 1);
setLocalDocs(sortedDocs);
} else {
setLocalDocs(documents);
}
}
return (
<>
<table className={`w-full border-collapse border-0 text-sm mt-3 ${s.documentTable}`}>
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-medium text-xs uppercase">
<tr>
<td className='w-12'>#</td>
<td>{t('datasetDocuments.list.table.header.fileName')}</td>
<td className='w-24'>{t('datasetDocuments.list.table.header.words')}</td>
<td className='w-24'>{t('datasetDocuments.list.table.header.hitCount')}</td>
<td className='w-44'>
<div className='flex justify-between items-center'>
{t('datasetDocuments.list.table.header.uploadTime')}
<ArrowDownIcon className={cn('h-3 w-3 stroke-current stroke-2 cursor-pointer', enableSort ? 'text-gray-500' : 'text-gray-300')} onClick={onClickSort} />
</div>
</td>
<td className='w-40'>{t('datasetDocuments.list.table.header.status')}</td>
<td className='w-20'>{t('datasetDocuments.list.table.header.action')}</td>
</tr>
</thead>
<tbody className="text-gray-700">
{localDocs.map((doc) => {
const suffix = doc.name.split('.').pop() || 'txt'
return <tr
key={doc.id}
className={'border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'}
onClick={() => {
router.push(`datasets/${datasetId}/documents/${doc.id}`)
}}>
<td className='text-left align-middle text-gray-500 text-xs'>{doc.position}</td>
<td className={s.tdValue}>
<div className={cn(s[`${doc?.data_source_info?.upload_file?.extension ?? suffix}Icon`], s.commonIcon, 'mr-1.5')}></div>
<span>{doc?.name?.replace(/\.[^/.]+$/, "")}<span className='text-gray-500'>.{suffix}</span></span>
</td>
<td>{renderCount(doc.word_count)}</td>
<td>{renderCount(doc.hit_count)}</td>
<td className='text-gray-500 text-[13px]'>
{dayjs.unix(doc.created_at).format(t('datasetHitTesting.dateTimeFormat') as string)}
</td>
<td>
<StatusItem status={doc.display_status} />
</td>
<td>
<OperationAction
datasetId={datasetId}
detail={pick(doc, ['enabled', 'archived', 'id'])}
onUpdate={onUpdate}
/>
</td>
</tr>
})}
</tbody>
</table>
</>
)
}
export default DocumentList

View File

@@ -0,0 +1,103 @@
.documentTable tbody td {
padding: 5px 10px 5px 12px;
box-sizing: border-box;
max-width: 200px;
}
.documentTable thead td {
padding: 0px 10px 0px 12px;
box-sizing: border-box;
max-width: 200px;
}
.title {
@apply text-xl font-medium text-gray-900;
}
.desc {
@apply text-sm font-normal text-gray-500;
}
.actionIconWrapperList {
@apply h-6 w-6 rounded-md border-none p-1 hover:bg-gray-100 !important;
}
.actionIconWrapperDetail {
@apply h-8 w-8 p-2 hover:bg-gray-50 border border-gray-200 hover:border-gray-300 hover:shadow-[0_1px_2px_rgba(16,24,40,0.05)] !important;
}
.actionItem {
@apply h-9 py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer;
}
.deleteActionItem {
@apply hover:bg-red-50 !important;
}
.actionName {
@apply text-gray-700 text-sm;
}
.addFileBtn {
@apply mt-4 w-fit !text-[13px] text-primary-600 font-medium bg-white border-[0.5px];
}
.plusIcon {
@apply w-4 h-4 mr-2 stroke-current stroke-[1.5px];
}
.emptyWrapper {
@apply flex items-center justify-center h-full;
}
.emptyElement {
@apply bg-gray-50 w-[560px] h-fit box-border px-5 py-4 rounded-2xl;
}
.emptyTitle {
@apply text-gray-700 font-semibold;
}
.emptyTip {
@apply mt-2 text-gray-500 text-sm font-normal;
}
.emptySymbolIconWrapper {
@apply w-[44px] h-[44px] border border-solid border-gray-100 rounded-lg flex items-center justify-center mb-2;
}
.commonIcon {
@apply w-4 h-4 inline-block align-middle;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
.actionIcon {
@apply bg-gray-500;
mask-image: url(./assets/action.svg);
}
.pdfIcon {
background-image: url(./assets/pdf.svg);
}
.jsonIcon {
background-image: url(./assets/json.svg);
}
.htmlIcon {
background-image: url(./assets/html.svg);
}
.txtIcon {
background-image: url(./assets/txt.svg);
}
.mdIcon {
background-image: url(./assets/md.svg);
}
.statusItemDetail {
@apply h-8 font-medium border border-gray-200 inline-flex items-center rounded-lg pl-3 pr-4 mr-2;
}
.tdValue {
@apply text-sm overflow-hidden text-ellipsis whitespace-nowrap;
}
.delModal {
background: linear-gradient(
180deg,
rgba(217, 45, 32, 0.05) 0%,
rgba(217, 45, 32, 0) 24.02%
),
#f9fafb;
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08),
0px 8px 8px -4px rgba(16, 24, 40, 0.03);
@apply rounded-2xl p-8;
}
.warningWrapper {
box-shadow: 0px 20px 24px -4px rgba(16, 24, 40, 0.08),
0px 8px 8px -4px rgba(16, 24, 40, 0.03);
background: rgba(255, 255, 255, 0.9);
@apply h-12 w-12 border-[0.5px] border-gray-100 rounded-xl mb-3 flex items-center justify-center;
}
.warningIcon {
@apply w-[22px] h-[22px] fill-current text-red-600;
}

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.9166 9.58333L17.2505 11.25L15.5833 9.58333M17.4542 10.8333C17.4845 10.5597 17.5 10.2817 17.5 10C17.5 5.85786 14.1421 2.5 10 2.5C5.85786 2.5 2.5 5.85786 2.5 10C2.5 14.1421 5.85786 17.5 10 17.5C12.3561 17.5 14.4584 16.4136 15.8333 14.7144M10 5.83333V10L12.5 11.6667" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 466 B

View File

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.9 1.75H2.68333C2.35664 1.75 2.19329 1.75 2.06851 1.81358C1.95874 1.86951 1.86951 1.95874 1.81358 2.06851C1.75 2.19329 1.75 2.35664 1.75 2.68333V4.9C1.75 5.2267 1.75 5.39005 1.81358 5.51483C1.86951 5.62459 1.95874 5.71383 2.06851 5.76975C2.19329 5.83333 2.35664 5.83333 2.68333 5.83333H4.9C5.2267 5.83333 5.39005 5.83333 5.51483 5.76975C5.62459 5.71383 5.71383 5.62459 5.76975 5.51483C5.83333 5.39005 5.83333 5.2267 5.83333 4.9V2.68333C5.83333 2.35664 5.83333 2.19329 5.76975 2.06851C5.71383 1.95874 5.62459 1.86951 5.51483 1.81358C5.39005 1.75 5.2267 1.75 4.9 1.75Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3167 1.75H9.1C8.7733 1.75 8.60995 1.75 8.48517 1.81358C8.37541 1.86951 8.28617 1.95874 8.23025 2.06851C8.16667 2.19329 8.16667 2.35664 8.16667 2.68333V4.9C8.16667 5.2267 8.16667 5.39005 8.23025 5.51483C8.28617 5.62459 8.37541 5.71383 8.48517 5.76975C8.60995 5.83333 8.7733 5.83333 9.1 5.83333H11.3167C11.6434 5.83333 11.8067 5.83333 11.9315 5.76975C12.0413 5.71383 12.1305 5.62459 12.1864 5.51483C12.25 5.39005 12.25 5.2267 12.25 4.9V2.68333C12.25 2.35664 12.25 2.19329 12.1864 2.06851C12.1305 1.95874 12.0413 1.86951 11.9315 1.81358C11.8067 1.75 11.6434 1.75 11.3167 1.75Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3167 8.16667H9.1C8.7733 8.16667 8.60995 8.16667 8.48517 8.23025C8.37541 8.28617 8.28617 8.37541 8.23025 8.48517C8.16667 8.60995 8.16667 8.7733 8.16667 9.1V11.3167C8.16667 11.6434 8.16667 11.8067 8.23025 11.9315C8.28617 12.0413 8.37541 12.1305 8.48517 12.1864C8.60995 12.25 8.7733 12.25 9.1 12.25H11.3167C11.6434 12.25 11.8067 12.25 11.9315 12.1864C12.0413 12.1305 12.1305 12.0413 12.1864 11.9315C12.25 11.8067 12.25 11.6434 12.25 11.3167V9.1C12.25 8.7733 12.25 8.60995 12.1864 8.48517C12.1305 8.37541 12.0413 8.28617 11.9315 8.23025C11.8067 8.16667 11.6434 8.16667 11.3167 8.16667Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.9 8.16667H2.68333C2.35664 8.16667 2.19329 8.16667 2.06851 8.23025C1.95874 8.28617 1.86951 8.37541 1.81358 8.48517C1.75 8.60995 1.75 8.7733 1.75 9.1V11.3167C1.75 11.6434 1.75 11.8067 1.81358 11.9315C1.86951 12.0413 1.95874 12.1305 2.06851 12.1864C2.19329 12.25 2.35664 12.25 2.68333 12.25H4.9C5.2267 12.25 5.39005 12.25 5.51483 12.1864C5.62459 12.1305 5.71383 12.0413 5.76975 11.9315C5.83333 11.8067 5.83333 11.6434 5.83333 11.3167V9.1C5.83333 8.7733 5.83333 8.60995 5.76975 8.48517C5.71383 8.37541 5.62459 8.28617 5.51483 8.23025C5.39005 8.16667 5.2267 8.16667 4.9 8.16667Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,10 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_4151_6854)">
<path d="M4.37484 2.62484C4.37484 1.81942 5.02776 1.1665 5.83317 1.1665C6.63859 1.1665 7.2915 1.81942 7.2915 2.62484V3.49984H7.87484C8.69023 3.49984 9.09793 3.49984 9.41953 3.63305C9.84833 3.81066 10.189 4.15134 10.3666 4.58014C10.4998 4.90174 10.4998 5.30944 10.4998 6.12484H11.3748C12.1803 6.12484 12.8332 6.77776 12.8332 7.58317C12.8332 8.38859 12.1803 9.0415 11.3748 9.0415H10.4998V10.0332C10.4998 11.0133 10.4998 11.5033 10.3091 11.8777C10.1413 12.2069 9.8736 12.4747 9.54432 12.6424C9.16997 12.8332 8.67993 12.8332 7.69984 12.8332H7.2915V11.8123C7.2915 11.0875 6.70388 10.4998 5.979 10.4998C5.25413 10.4998 4.6665 11.0875 4.6665 11.8123V12.8332H3.9665C2.98641 12.8332 2.49637 12.8332 2.12202 12.6424C1.79274 12.4747 1.52502 12.2069 1.35724 11.8777C1.1665 11.5033 1.1665 11.0133 1.1665 10.0332V9.0415H2.0415C2.84692 9.0415 3.49984 8.38859 3.49984 7.58317C3.49984 6.77776 2.84692 6.12484 2.0415 6.12484H1.1665C1.1665 5.30944 1.1665 4.90174 1.29971 4.58014C1.47733 4.15134 1.81801 3.81066 2.24681 3.63305C2.56841 3.49984 2.97611 3.49984 3.7915 3.49984H4.37484V2.62484Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_4151_6854">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,99 @@
import React, { FC } from "react";
import cn from "classnames";
import { SegmentDetailModel } from "@/models/datasets";
import { useTranslation } from "react-i18next";
import Divider from "@/app/components/base/divider";
import { SegmentIndexTag } from "../documents/detail/completed";
import s from "../documents/detail/completed/style.module.css";
import ReactECharts from "echarts-for-react";
type IScatterChartProps = {
data: Array<number[]>
curr: Array<number[]>
}
const ScatterChart: FC<IScatterChartProps> = ({ data, curr }) => {
const option = {
xAxis: {},
yAxis: {},
tooltip: {
trigger: 'item',
axisPointer: {
type: 'cross'
}
},
series: [
{
type: 'effectScatter',
symbolSize: 5,
data: curr,
},
{
type: 'scatter',
symbolSize: 5,
data,
}
]
};
return (
<ReactECharts option={option} style={{ height: 380, width: 430 }} />
)
}
type IHitDetailProps = {
segInfo?: Partial<SegmentDetailModel> & { id: string };
vectorInfo?: { curr: Array<number[]>; points: Array<number[]> };
};
const HitDetail: FC<IHitDetailProps> = ({ segInfo, vectorInfo }) => {
const { t } = useTranslation();
return (
<div className={"flex flex-row"}>
<div className="flex-1 bg-gray-25 p-6">
<div className="flex items-center">
<SegmentIndexTag
positionId={segInfo?.position || ""}
className="w-fit mr-6"
/>
<div className={cn(s.commonIcon, s.typeSquareIcon)} />
<span className={cn("mr-6", s.numberInfo)}>
{segInfo?.word_count} {t("datasetDocuments.segment.characters")}
</span>
<div className={cn(s.commonIcon, s.targetIcon)} />
<span className={s.numberInfo}>
{segInfo?.hit_count} {t("datasetDocuments.segment.hitCount")}
</span>
</div>
<Divider />
<div className={s.segModalContent}>{segInfo?.content}</div>
<div className={s.keywordTitle}>
{t("datasetDocuments.segment.keywords")}
</div>
<div className={s.keywordWrapper}>
{!segInfo?.keywords?.length
? "-"
: segInfo?.keywords?.map((word: any) => {
return <div className={s.keyword}>{word}</div>;
})}
</div>
</div>
<div className="flex-1 bg-white p-6">
<div className="flex items-center">
<div className={cn(s.commonIcon, s.bezierCurveIcon)} />
<span className={s.numberInfo}>
{t("datasetDocuments.segment.vectorHash")}
</span>
</div>
<div
className={cn(s.numberInfo, "w-[400px] truncate text-gray-700 mt-1")}
>
{segInfo?.index_node_hash}
</div>
<ScatterChart data={vectorInfo?.points || []} curr={vectorInfo?.curr || []} />
</div>
</div>
);
};
export default HitDetail;

View File

@@ -0,0 +1,174 @@
'use client'
import React, { useState, FC, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import { fetchTestingRecords } from '@/service/datasets'
import { omit } from 'lodash-es'
import Pagination from '@/app/components/base/pagination'
import Modal from '@/app/components/base/modal'
import Loading from '@/app/components/base/loading'
import type { HitTestingResponse, HitTesting } from '@/models/datasets'
import cn from 'classnames'
import dayjs from 'dayjs'
import SegmentCard from '../documents/detail/completed/SegmentCard'
import docStyle from '../documents/detail/completed/style.module.css'
import Textarea from './textarea'
import s from './style.module.css'
import HitDetail from './hit-detail'
const limit = 10;
type Props = {
datasetId: string
}
const RecordsEmpty: FC = () => {
const { t } = useTranslation()
return <div className='bg-gray-50 rounded-2xl p-5'>
<div className={s.clockWrapper}>
<div className={cn(s.clockIcon, 'w-5 h-5')}></div>
</div>
<div className='my-2 text-gray-500 text-sm'>{t('datasetHitTesting.noRecentTip')}</div>
</div>
}
const HitTesting: FC<Props> = ({ datasetId }: Props) => {
const { t } = useTranslation()
const [hitResult, setHitResult] = useState<HitTestingResponse | undefined>(); // 初始化记录为空数组
const [submitLoading, setSubmitLoading] = useState(false);
const [currParagraph, setCurrParagraph] = useState<{ paraInfo?: HitTesting; showModal: boolean }>({ showModal: false })
const [text, setText] = useState('');
const [currPage, setCurrPage] = React.useState<number>(0)
const { data: recordsRes, error, mutate: recordsMutate } = useSWR({
action: 'fetchTestingRecords',
datasetId,
params: { limit, page: currPage + 1, }
}, apiParams => fetchTestingRecords(omit(apiParams, 'action')))
const total = recordsRes?.total || 0
const points = useMemo(() => (hitResult?.records.map((v) => [v.tsne_position.x, v.tsne_position.y]) || []), [hitResult?.records])
const onClickCard = (detail: HitTesting) => {
setCurrParagraph({ paraInfo: detail, showModal: true })
}
return (
<div className={s.container}>
<div className={s.leftDiv}>
<div className={s.titleWrapper}>
<h1 className={s.title}>{t('datasetHitTesting.title')}</h1>
<p className={s.desc}>{t('datasetHitTesting.desc')}</p>
</div>
<Textarea
datasetId={datasetId}
setHitResult={setHitResult}
onUpdateList={recordsMutate}
loading={submitLoading}
setLoading={setSubmitLoading}
setText={setText}
text={text}
/>
<div className={cn(s.title, 'mt-8 mb-2')}>{t('datasetHitTesting.recents')}</div>
{!recordsRes && !error ? (
<div className='flex-1'><Loading type='app' /></div>
) : recordsRes?.data?.length ? (
<>
<table className={`w-full border-collapse border-0 mt-3 ${s.table}`}>
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold">
<tr>
<td className='w-28'>{t('datasetHitTesting.table.header.source')}</td>
<td>{t('datasetHitTesting.table.header.text')}</td>
<td className='w-48'>{t('datasetHitTesting.table.header.time')}</td>
</tr>
</thead>
<tbody className="text-gray-500">
{recordsRes?.data?.map((record) => {
return <tr
key={record.id}
className='group border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'
onClick={() => setText(record.content)}
>
<td className='w-24'>
<div className='flex items-center'>
<div className={cn(s[`${record.source}_icon`], s.commonIcon, 'mr-1')} />
<span className='capitalize'>{record.source.replace('_', ' ')}</span>
</div>
</td>
<td className='max-w-xs group-hover:text-primary-600'>{record.content}</td>
<td className='w-36'>
{dayjs.unix(record.created_at).format(t('datasetHitTesting.dateTimeFormat') as string)}
</td>
</tr>
})}
</tbody>
</table>
{(total && total > limit)
? <Pagination current={currPage} onChange={setCurrPage} total={total} limit={limit} />
: null}
</>
) : (
<RecordsEmpty />
)}
</div>
<div className={s.rightDiv}>
{submitLoading ?
<div className={s.cardWrapper}>
<SegmentCard
loading={true}
scene='hitTesting'
className='h-[216px]'
/>
<SegmentCard
loading={true}
scene='hitTesting'
className='h-[216px]'
/>
</div> : !hitResult?.records.length ? (
<div className='h-full flex flex-col justify-center items-center'>
<div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!bg-gray-200 !h-14 !w-14')} />
<div className='text-gray-300 text-[13px] mt-3'>
{t('datasetHitTesting.hit.emptyTip')}
</div>
</div>
) : (
<>
<div className='text-gray-600 font-semibold mb-4'>{t('datasetHitTesting.hit.title')}</div>
<div className='overflow-auto flex-1'>
<div className={s.cardWrapper}>
{hitResult?.records.map((record) => {
return <SegmentCard
loading={false}
detail={record.segment as any}
score={record.score}
scene='hitTesting'
className='h-[216px] mb-4'
onClick={() => onClickCard(record as any)}
/>
})}
</div>
</div>
</>
)
}
</div>
<Modal
className='!max-w-[960px] !p-0'
closable
onClose={() => setCurrParagraph({ showModal: false })}
isShow={currParagraph.showModal}
>
{currParagraph.showModal && <HitDetail
segInfo={currParagraph.paraInfo?.segment}
vectorInfo={{
curr: [[currParagraph.paraInfo?.tsne_position?.x || 0, currParagraph.paraInfo?.tsne_position.y || 0]],
points,
}}
/>}
</Modal>
</div>
)
}
export default HitTesting

View File

@@ -0,0 +1,65 @@
.container {
@apply flex h-full w-full relative;
}
.container > div {
@apply flex-1 h-full;
}
.leftDiv {
@apply border-r border-gray-100 px-6 py-3 flex flex-col;
}
.rightDiv {
@apply px-8 pt-[42px] pb-[26px] flex flex-col;
}
.titleWrapper {
@apply flex flex-col justify-center gap-1 mb-5;
}
.title {
@apply text-xl font-medium text-gray-900;
}
.desc {
@apply text-sm font-normal text-gray-500;
}
.textarea {
min-height: 96px;
@apply border-none resize-none font-normal caret-primary-600 text-gray-700 text-sm w-full bg-gray-25 focus-visible:outline-none placeholder:text-gray-300 placeholder:text-sm placeholder:font-normal !important;
}
.table {
@apply text-[13px] text-gray-500;
}
.table td {
@apply whitespace-nowrap overflow-hidden text-ellipsis;
}
.commonIcon {
@apply w-3.5 h-3.5 inline-block align-middle;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
.app_icon {
background-image: url(./assets/grid.svg);
}
.hit_testing_icon {
background-image: url(../documents/assets/target.svg);
}
.plugin_icon {
background-image: url(./assets/plugin.svg);
}
.wrapper {
@apply relative border border-primary-600 min-h-[200px] rounded-xl pt-3 pb-14 px-4 bg-gray-25;
}
.cardWrapper {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(284px, auto));
grid-gap: 16px;
grid-auto-rows: 216px;
}
.clockWrapper {
border: 0.5px solid #eaecf5;
@apply rounded-lg w-11 h-11 flex justify-center items-center;
}
.clockIcon {
mask-image: url(./assets/clock.svg);
@apply bg-gray-500;
}

View File

@@ -0,0 +1,116 @@
import type { FC } from "react";
import { useContext } from 'use-context-selector'
import { DocumentTextIcon } from "@heroicons/react/24/solid";
import { useTranslation } from "react-i18next";
import { hitTesting } from "@/service/datasets";
import DatasetDetailContext from '@/context/dataset-detail'
import { HitTestingResponse } from "@/models/datasets";
import cn from "classnames";
import Button from "../../base/button";
import Tag from "../../base/tag";
import Tooltip from "../../base/tooltip";
import s from "./style.module.css";
import { asyncRunSafe } from "@/utils";
type Props = {
datasetId: string;
onUpdateList: () => void;
setHitResult: (res: HitTestingResponse) => void;
loading: boolean;
setLoading: (v: boolean) => void;
text: string;
setText: (v: string) => void;
};
const TextAreaWithButton: FC<Props> = ({
datasetId,
onUpdateList,
setHitResult,
setLoading,
loading,
text,
setText,
}) => {
const { t } = useTranslation();
const { indexingTechnique } = useContext(DatasetDetailContext)
// 处理文本框内容变化的函数
function handleTextChange(event: any) {
setText(event.target.value);
}
// 处理按钮点击的函数
const onSubmit = async () => {
setLoading(true);
const [e, res] = await asyncRunSafe<HitTestingResponse>(
hitTesting({ datasetId, queryText: text }) as Promise<HitTestingResponse>
);
if (!e) {
setHitResult(res);
onUpdateList?.();
}
setLoading(false);
};
return (
<>
<div className={s.wrapper}>
<div className="flex items-center mb-3">
<DocumentTextIcon className="w-4 h-4 text-primary-600 mr-2" />
<span className="text-gray-800 font-semibold text-sm">
{t("datasetHitTesting.input.title")}
</span>
</div>
<textarea
value={text}
onChange={handleTextChange}
placeholder={t("datasetHitTesting.input.placeholder") as string}
className={s.textarea}
/>
<div className="absolute inset-x-0 bottom-0 flex items-center justify-between mx-4 mt-2 mb-4">
{text?.length > 200 ? (
<Tooltip
content={t("datasetHitTesting.input.countWarning") as string}
selector="hit-testing-warning"
>
<div>
<Tag color="red" className="!text-red-600">
{text?.length}
<span className="text-red-300 mx-0.5">/</span>
200
</Tag>
</div>
</Tooltip>
) : (
<Tag
color="gray"
className={cn("!text-gray-500", text?.length ? "" : "opacity-50")}
>
{text?.length}
<span className="text-gray-300 mx-0.5">/</span>
200
</Tag>
)}
<Tooltip
selector="hit-testing-submit"
disabled={indexingTechnique === 'high_quality'}
content={t("datasetHitTesting.input.indexWarning") as string}
>
<div>
<Button
onClick={onSubmit}
type="primary"
loading={loading}
disabled={indexingTechnique !== 'high_quality' ? true : (!text?.length || text?.length > 200)}
>
{t("datasetHitTesting.input.testing")}
</Button>
</div>
</Tooltip>
</div>
</div>
</>
);
};
export default TextAreaWithButton;

View File

@@ -0,0 +1,127 @@
'use client'
import { useState } from 'react'
import { useContext } from 'use-context-selector'
import { BookOpenIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import { ToastContext } from '@/app/components/base/toast'
import PermissionsRadio from '../permissions-radio'
import IndexMethodRadio from '../index-method-radio'
import Button from '@/app/components/base/button'
import { useDatasetsContext } from '@/context/datasets-context'
import { updateDatasetSetting } from '@/service/datasets'
const rowClass = `
flex justify-between py-4
`
const labelClass = `
flex items-center w-[168px] h-9
`
const inputClass = `
w-[480px] px-3 bg-gray-100 text-sm text-gray-800 rounded-lg outline-none appearance-none
`
const Form = () => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { currentDataset, mutateDatasets } = useDatasetsContext()
const [loading, setLoading] = useState(false)
const [name, setName] = useState(currentDataset?.name)
const [description, setDescription] = useState(currentDataset?.description)
const [permission, setPermission] = useState(currentDataset?.permission)
const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique)
const handleSave = async () => {
if (loading) return
if (!name?.trim()) {
notify({ type: 'error', message: t('datasetSettings.form.nameError') })
return
}
try {
setLoading(true)
await updateDatasetSetting({
datasetId: currentDataset!.id,
body: {
name,
description,
permission,
indexing_technique: indexMethod
}
})
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutateDatasets()
} catch (e) {
notify({ type: 'error', message: t('common.actionMsg.modificationFailed') })
} finally {
setLoading(false)
}
}
return (
<div className='w-[800px] px-16 py-6'>
<div className={rowClass}>
<div className={labelClass}>
<div>{t('datasetSettings.form.name')}</div>
</div>
<input
className={inputClass}
value={name}
onChange={e => setName(e.target.value)}
/>
</div>
<div className={rowClass}>
<div className={labelClass}>
<div>{t('datasetSettings.form.desc')}</div>
</div>
<div>
<textarea
className={`${inputClass} block mb-2 h-[120px] py-2 resize-none`}
placeholder={t('datasetSettings.form.descPlaceholder') || ''}
value={description}
onChange={e => setDescription(e.target.value)}
/>
<a className='flex items-center h-[18px] px-3 text-xs text-gray-500' href="https://docs.dify.ai/advanced/datasets#how-to-write-a-good-dataset-description" target='_blank'>
<BookOpenIcon className='w-3 h-[18px] mr-1' />
{t('datasetSettings.form.descWrite')}
</a>
</div>
</div>
<div className={rowClass}>
<div className={labelClass}>
<div>{t('datasetSettings.form.permissions')}</div>
</div>
<div className='w-[480px]'>
<PermissionsRadio
value={permission}
onChange={v => setPermission(v)}
/>
</div>
</div>
<div className='w-full h-0 border-b-[0.5px] border-b-gray-200 my-2' />
<div className={rowClass}>
<div className={labelClass}>
<div>{t('datasetSettings.form.indexMethod')}</div>
</div>
<div className='w-[480px]'>
<IndexMethodRadio
value={indexMethod}
onChange={v => setIndexMethod(v)}
/>
</div>
</div>
<div className={rowClass}>
<div className={labelClass} />
<div className='w-[480px]'>
<Button
className='min-w-24 text-sm'
type='primary'
onClick={handleSave}
>
{t('datasetSettings.form.save')}
</Button>
</div>
</div>
</div>
)
}
export default Form

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="8" fill="#EEF4FF"/>
<path d="M11.6665 9.99998C12.9552 9.99998 13.9998 8.95531 13.9998 7.66665C13.9998 6.37798 12.9552 5.33331 11.6665 5.33331C10.3778 5.33331 9.33317 6.37798 9.33317 7.66665C9.33317 8.95531 10.3778 9.99998 11.6665 9.99998Z" fill="#444CE7"/>
<path d="M8.65017 9.75198C8.49106 9.52227 8.4115 9.40741 8.32581 9.36947C8.24888 9.33541 8.1679 9.33124 8.08788 9.35723C7.99875 9.38618 7.92865 9.46797 7.78845 9.63154C7.22585 10.2879 6.84213 11.1027 6.71374 12.0001H6.6665C6.29831 12.0001 5.99984 11.7016 5.99984 11.3334C5.99984 11.0875 6.13265 10.8718 6.33365 10.7555C6.65236 10.5712 6.76127 10.1634 6.57691 9.84466C6.39255 9.52595 5.98473 9.41704 5.66602 9.60141C5.06996 9.94621 4.6665 10.5923 4.6665 11.3334C4.6665 12.438 5.56193 13.3334 6.6665 13.3334H6.71377C6.85773 14.3389 7.32239 15.2415 7.9998 15.9328L7.99979 17.4822C7.99976 17.5616 7.99973 17.6565 8.00655 17.74C8.01446 17.8368 8.03476 17.9755 8.10879 18.1208C8.20466 18.309 8.35764 18.4619 8.54581 18.5578C8.6911 18.6318 8.82976 18.6521 8.92658 18.6601C9.0101 18.6669 9.10492 18.6668 9.18432 18.6668H10.4819C10.5613 18.6668 10.6562 18.6669 10.7397 18.6601C10.8365 18.6521 10.9752 18.6318 11.1205 18.5578C11.3086 18.4619 11.4616 18.309 11.5575 18.1208C11.6315 17.9755 11.6518 17.8368 11.6597 17.74C11.6665 17.6565 11.6665 17.5616 11.6665 17.4822L11.6665 17.3335H12.3331L12.3331 17.482C12.3331 17.5614 12.3331 17.6562 12.3399 17.7398C12.3478 17.8366 12.3681 17.9753 12.4421 18.1205C12.538 18.3087 12.691 18.4617 12.8791 18.5576C13.0244 18.6316 13.1631 18.6519 13.2599 18.6598C13.3434 18.6666 13.4382 18.6666 13.5176 18.6666H14.8153C14.8947 18.6666 14.9896 18.6666 15.0731 18.6598C15.1699 18.6519 15.3085 18.6316 15.4538 18.5576C15.642 18.4617 15.795 18.3087 15.8909 18.1205C15.9649 17.9753 15.9852 17.8366 15.9931 17.7398C15.9999 17.6562 15.9999 17.5614 15.9999 17.482L15.9999 16.884C16.7373 16.5337 17.3676 15.9963 17.83 15.3332L18.1486 15.3332C18.228 15.3333 18.3229 15.3333 18.4064 15.3265C18.5032 15.3186 18.6419 15.2983 18.7872 15.2242C18.9753 15.1284 19.1283 14.9754 19.2242 14.7872C19.2982 14.6419 19.3185 14.5033 19.3264 14.4064C19.3333 14.3229 19.3332 14.2281 19.3332 14.1487V11.8424C19.3332 11.7668 19.3332 11.6765 19.327 11.5968C19.3199 11.5047 19.3015 11.3725 19.2341 11.2326C19.1358 11.0286 18.9712 10.8639 18.7671 10.7656C18.6272 10.6982 18.4951 10.6799 18.403 10.6727C18.3435 10.6681 18.2781 10.6669 18.2173 10.6666C17.9935 10.1955 17.6934 9.76819 17.3332 9.40057L17.3332 8.68818C17.3332 8.58496 17.3333 8.46813 17.3243 8.36763C17.3144 8.25667 17.2886 8.08512 17.1832 7.91509C17.0519 7.70309 16.846 7.54783 16.6061 7.47976C16.4137 7.42517 16.2416 7.44747 16.1322 7.46844C16.0331 7.48743 15.9208 7.51956 15.8216 7.54795L15.7177 7.57763C15.581 7.61667 15.5127 7.6362 15.4646 7.67139C15.4182 7.70536 15.3899 7.73885 15.364 7.7902C15.3371 7.84338 15.3279 7.92449 15.3094 8.08672C15.101 9.91393 13.5495 11.3333 11.6665 11.3333C10.4162 11.3333 9.31201 10.7075 8.65017 9.75198Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,12 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="8" fill="#FFF6ED"/>
<path d="M11.9998 4.66669C12.368 4.66669 12.6665 4.96516 12.6665 5.33335V6.66669C12.6665 7.03488 12.368 7.33335 11.9998 7.33335C11.6316 7.33335 11.3332 7.03488 11.3332 6.66669V5.33335C11.3332 4.96516 11.6316 4.66669 11.9998 4.66669Z" fill="#FB6514"/>
<path d="M7.75705 6.81459C7.4967 6.55424 7.07459 6.55424 6.81424 6.81459C6.55389 7.07494 6.55389 7.49705 6.81424 7.75739L7.75705 8.7002C8.0174 8.96055 8.43951 8.96055 8.69986 8.7002C8.96021 8.43985 8.96021 8.01774 8.69986 7.75739L7.75705 6.81459Z" fill="#FB6514"/>
<path d="M4.6665 12C4.6665 11.6318 4.96498 11.3334 5.33317 11.3334H6.6665C7.03469 11.3334 7.33317 11.6318 7.33317 12C7.33317 12.3682 7.03469 12.6667 6.6665 12.6667H5.33317C4.96498 12.6667 4.6665 12.3682 4.6665 12Z" fill="#FB6514"/>
<path d="M17.3332 11.3334C16.965 11.3334 16.6665 11.6318 16.6665 12C16.6665 12.3682 16.965 12.6667 17.3332 12.6667H18.6665C19.0347 12.6667 19.3332 12.3682 19.3332 12C19.3332 11.6318 19.0347 11.3334 18.6665 11.3334H17.3332Z" fill="#FB6514"/>
<path d="M16.2424 15.2998C15.982 15.0394 15.5599 15.0394 15.2996 15.2998C15.0392 15.5601 15.0392 15.9822 15.2996 16.2426L16.2424 17.1854C16.5027 17.4457 16.9249 17.4457 17.1852 17.1854C17.4456 16.925 17.4456 16.5029 17.1852 16.2426L16.2424 15.2998Z" fill="#FB6514"/>
<path d="M17.1852 7.75739C17.4456 7.49705 17.4456 7.07494 17.1852 6.81459C16.9249 6.55424 16.5027 6.55424 16.2424 6.81459L15.2996 7.75739C15.0392 8.01774 15.0392 8.43985 15.2996 8.7002C15.5599 8.96055 15.982 8.96055 16.2424 8.7002L17.1852 7.75739Z" fill="#FB6514"/>
<path d="M11.9998 16.6667C12.368 16.6667 12.6665 16.9652 12.6665 17.3334V18.6667C12.6665 19.0349 12.368 19.3334 11.9998 19.3334C11.6316 19.3334 11.3332 19.0349 11.3332 18.6667V17.3334C11.3332 16.9652 11.6316 16.6667 11.9998 16.6667Z" fill="#FB6514"/>
<path d="M8.69986 16.2426C8.96021 15.9822 8.96021 15.5601 8.69986 15.2998C8.43951 15.0394 8.0174 15.0394 7.75705 15.2998L6.81424 16.2426C6.55389 16.5029 6.55389 16.925 6.81424 17.1854C7.07459 17.4457 7.4967 17.4457 7.75705 17.1854L8.69986 16.2426Z" fill="#FB6514"/>
<path d="M12.5977 8.3716C12.4853 8.14407 12.2536 8.00002 11.9999 8.00002C11.7461 8.00002 11.5144 8.14407 11.4021 8.3716L10.527 10.1443L8.5701 10.4304C8.31906 10.4671 8.11061 10.6431 8.03236 10.8844C7.95411 11.1257 8.01962 11.3906 8.20137 11.5676L9.61684 12.9463L9.28278 14.894C9.23988 15.1441 9.34271 15.3969 9.54803 15.5461C9.75335 15.6952 10.0255 15.7149 10.2502 15.5967L11.9999 14.6766L13.7496 15.5967C13.9742 15.7149 14.2464 15.6952 14.4517 15.5461C14.657 15.3969 14.7598 15.1441 14.7169 14.894L14.3829 12.9463L15.7983 11.5676C15.9801 11.3906 16.0456 11.1257 15.9674 10.8844C15.8891 10.6431 15.6806 10.4671 15.4296 10.4304L13.4727 10.1443L12.5977 8.3716Z" fill="#FB6514"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,38 @@
.icon {
margin-right: 12px;
width: 24px;
height: 24px;
background: center center no-repeat;
background-size: contain;
}
.high-quality-icon {
background-image: url(./assets/high-quality.svg);
}
.economy-icon {
background-image: url(./assets/economy.svg);
}
.wrapper .item:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.wrapper .item-active {
background-color: #ffffff;
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.wrapper .item-active .radio {
border-width: 5px;
border-color: #155EEF;
}
.wrapper .item-active:hover {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}

View File

@@ -0,0 +1,64 @@
'use client'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import s from './index.module.css'
import { DataSet } from '@/models/datasets'
const itemClass = `
w-[234px] p-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
`
const radioClass = `
w-4 h-4 border-[2px] border-gray-200 rounded-full
`
type IIndexMethodRadioProps = {
value?: DataSet['indexing_technique']
onChange: (v?: DataSet['indexing_technique']) => void
}
const IndexMethodRadio = ({
value,
onChange
}: IIndexMethodRadioProps) => {
const { t } = useTranslation()
const options = [
{
key: 'high_quality',
text: t('datasetSettings.form.indexMethodHighQuality'),
desc: t('datasetSettings.form.indexMethodHighQualityTip'),
icon: 'high-quality'
},
{
key: 'economy',
text: t('datasetSettings.form.indexMethodEconomy'),
desc: t('datasetSettings.form.indexMethodEconomyTip'),
icon: 'economy'
}
]
return (
<div className={classNames(s.wrapper, 'flex justify-between w-full')}>
{
options.map(option => (
<div
key={option.key}
className={classNames(
option.key === value && s['item-active'],
s.item,
itemClass
)}
onClick={() => onChange(option.key as DataSet['indexing_technique'])}
>
<div className='flex items-center mb-1'>
<div className={classNames(s.icon, s[`${option.icon}-icon`])} />
<div className='grow text-sm text-gray-900'>{option.text}</div>
<div className={classNames(radioClass, s.radio)} />
</div>
<div className='pl-9 text-xs text-gray-500 leading-[18px]'>{option.desc}</div>
</div>
))
}
</div>
)
}
export default IndexMethodRadio

View File

@@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="8" fill="#EEF4FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4043 14.2586C15.5696 13.9296 15.9703 13.7969 16.2993 13.9622C17.3889 14.5095 18.31 15.381 18.9766 16.4548C19.0776 16.6174 19.2246 16.8347 19.2702 17.1291C19.3191 17.4443 19.2335 17.7457 19.1061 17.9749C18.9786 18.2041 18.7676 18.4357 18.4741 18.5605C18.1949 18.6791 17.8913 18.6666 17.6667 18.6666C17.2985 18.6666 17.0001 18.3682 17.0001 18C17.0001 17.6318 17.2985 17.3333 17.6667 17.3333C17.8102 17.3333 17.8856 17.3329 17.9395 17.3292L17.9409 17.3268C17.9536 17.3038 17.8568 17.1789 17.8438 17.158C17.2956 16.2749 16.5524 15.5814 15.7008 15.1536C15.3718 14.9884 15.2391 14.5877 15.4043 14.2586Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0697 6.01513C14.2336 5.68541 14.6337 5.55095 14.9634 5.71481C16.1691 6.314 17.0001 7.55934 17.0001 8.99998C17.0001 10.4406 16.1691 11.686 14.9634 12.2851C14.6337 12.449 14.2336 12.3145 14.0697 11.9848C13.9059 11.6551 14.0403 11.255 14.37 11.0911C15.14 10.7085 15.6667 9.91515 15.6667 8.99998C15.6667 8.08481 15.14 7.29144 14.37 6.90883C14.0403 6.74497 13.9059 6.34485 14.0697 6.01513Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66673 8.99998C6.66673 6.97494 8.30835 5.33331 10.3334 5.33331C12.3584 5.33331 14.0001 6.97494 14.0001 8.99998C14.0001 11.025 12.3584 12.6666 10.3334 12.6666C8.30835 12.6666 6.66673 11.025 6.66673 8.99998Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.3334 13.3333C12.4642 13.3333 14.3691 14.5361 15.5315 16.2801C15.6339 16.4337 15.7431 16.5976 15.8194 16.7533C15.9113 16.9407 15.9773 17.156 15.9619 17.4132C15.9496 17.6183 15.8816 17.8086 15.8007 17.9597C15.7198 18.1107 15.5991 18.2728 15.4352 18.3968C15.2157 18.5628 14.9791 18.621 14.77 18.6453C14.5858 18.6667 14.3677 18.6667 14.148 18.6667C11.6059 18.6662 9.06185 18.6662 6.51877 18.6667C6.29908 18.6667 6.08098 18.6667 5.89682 18.6453C5.68769 18.621 5.4511 18.5628 5.23155 18.3968C5.06767 18.2728 4.94702 18.1107 4.86612 17.9597C4.78523 17.8086 4.71719 17.6183 4.70488 17.4132C4.68945 17.156 4.75545 16.9407 4.84734 16.7533C4.92369 16.5976 5.0329 16.4337 5.13531 16.2801C6.2977 14.5361 8.20257 13.3333 10.3334 13.3333Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,30 @@
.user-icon {
width: 24px;
height: 24px;
background: url(./assets/user.svg) center center;
background-size: contain;
}
.wrapper .item:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.wrapper .item-active {
background-color: #ffffff;
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}
.wrapper .item-active .radio {
border-width: 5px;
border-color: #155EEF;
}
.wrapper .item-active:hover {
border-width: 1.5px;
border-color: #528BFF;
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
}

View File

@@ -0,0 +1,57 @@
'use client'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import s from './index.module.css'
import { DataSet } from '@/models/datasets'
const itemClass = `
flex items-center w-[234px] h-12 px-3 rounded-xl bg-gray-25 border border-gray-100 cursor-pointer
`
const radioClass = `
w-4 h-4 border-[2px] border-gray-200 rounded-full
`
type IPermissionsRadioProps = {
value?: DataSet['permission']
onChange: (v?: DataSet['permission']) => void
}
const PermissionsRadio = ({
value,
onChange
}: IPermissionsRadioProps) => {
const { t } = useTranslation()
const options = [
{
key: 'only_me',
text: t('datasetSettings.form.permissionsOnlyMe')
},
{
key: 'all_team_members',
text: t('datasetSettings.form.permissionsAllMember')
}
]
return (
<div className={classNames(s.wrapper, 'flex justify-between w-full')}>
{
options.map(option => (
<div
key={option.key}
className={classNames(
option.key === value && s['item-active'],
itemClass,
s.item
)}
onClick={() => onChange(option.key as DataSet['permission'])}
>
<div className={classNames(s['user-icon'], 'mr-3')} />
<div className='grow text-sm text-gray-900'>{option.text}</div>
<div className={classNames(radioClass, s.radio)} />
</div>
))
}
</div>
)
}
export default PermissionsRadio