Initial commit
5
web/app/components/datasets/documents/assets/action.svg
Normal 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 |
10
web/app/components/datasets/documents/assets/atSign.svg
Normal 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 |
@@ -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 |
@@ -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 |
@@ -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 |
15
web/app/components/datasets/documents/assets/cardLoading.svg
Normal 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 |
3
web/app/components/datasets/documents/assets/file.svg
Normal 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 |
10
web/app/components/datasets/documents/assets/globe.svg
Normal 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 |
@@ -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 |
15
web/app/components/datasets/documents/assets/hitLoading.svg
Normal 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 |
23
web/app/components/datasets/documents/assets/html.svg
Normal 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 |
23
web/app/components/datasets/documents/assets/json.svg
Normal 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 |
@@ -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 |
@@ -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 |
18
web/app/components/datasets/documents/assets/md.svg
Normal 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 |
@@ -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 |
4
web/app/components/datasets/documents/assets/normal.svg
Normal 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 |
22
web/app/components/datasets/documents/assets/pdf.svg
Normal 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 |
11
web/app/components/datasets/documents/assets/star.svg
Normal 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 |
10
web/app/components/datasets/documents/assets/target.svg
Normal 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 |
23
web/app/components/datasets/documents/assets/txt.svg
Normal 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 |
@@ -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 |
@@ -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;
|
@@ -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;
|
206
web/app/components/datasets/documents/detail/completed/index.tsx
Normal 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
|
@@ -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];
|
||||
}
|
268
web/app/components/datasets/documents/detail/embedding/index.tsx
Normal 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
|
@@ -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;
|
||||
}
|
121
web/app/components/datasets/documents/detail/index.tsx
Normal 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
|
363
web/app/components/datasets/documents/detail/metadata/index.tsx
Normal 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
|
@@ -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
|
||||
}
|
@@ -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;
|
||||
}
|
135
web/app/components/datasets/documents/index.tsx
Normal 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
|
318
web/app/components/datasets/documents/list.tsx
Normal 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') }, // completed,archived = false,enabled = true
|
||||
enabled: { color: 'green', text: t('datasetDocuments.list.status.enabled') }, // completed,archived = false,enabled = true
|
||||
disabled: { color: 'gray', text: t('datasetDocuments.list.status.disabled') }, // completed,archived = false,enabled = false
|
||||
archived: { color: 'gray', text: t('datasetDocuments.list.status.archived') }, // completed,archived = 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
|
103
web/app/components/datasets/documents/style.module.css
Normal 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;
|
||||
}
|