|
25 | 25 | import FeedbackMenu from './FeedbackMenu.svelte'; |
26 | 26 | import FeedbackModal from './FeedbackModal.svelte'; |
27 | 27 | import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte'; |
| 28 | + import Dropdown from '$lib/components/common/Dropdown.svelte'; |
28 | 29 |
|
29 | 30 | import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; |
30 | 31 | import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; |
|
148 | 149 | window.addEventListener('message', messageHandler, false); |
149 | 150 | }; |
150 | 151 |
|
151 | | - const exportHandler = async () => { |
| 152 | + const feedbacksToCsv = (feedbacks) => { |
| 153 | + const rows = feedbacks.map((f) => { |
| 154 | + const { data, ...rest } = f; |
| 155 | + return { |
| 156 | + id: rest.id, |
| 157 | + user_id: rest.user_id, |
| 158 | + chat_id: data?.chat_id ?? '', |
| 159 | + model_id: data?.model_id ?? '', |
| 160 | + sibling_model_ids: (data?.sibling_model_ids ?? []).join(';'), |
| 161 | + rating: data?.rating ?? '', |
| 162 | + reason: data?.reason ?? '', |
| 163 | + comment: data?.comment ?? '', |
| 164 | + created_at: rest.created_at, |
| 165 | + updated_at: rest.updated_at |
| 166 | + }; |
| 167 | + }); |
| 168 | +
|
| 169 | + if (rows.length === 0) return ''; |
| 170 | +
|
| 171 | + const headers = Object.keys(rows[0]); |
| 172 | + const escape = (val) => { |
| 173 | + const s = String(val ?? ''); |
| 174 | + return s.includes(',') || s.includes('"') || s.includes('\n') |
| 175 | + ? `"${s.replace(/"/g, '""')}"` |
| 176 | + : s; |
| 177 | + }; |
| 178 | +
|
| 179 | + return [ |
| 180 | + headers.join(','), |
| 181 | + ...rows.map((r) => headers.map((h) => escape(r[h])).join(',')) |
| 182 | + ].join('\n'); |
| 183 | + }; |
| 184 | +
|
| 185 | + const exportHandler = async (format: 'json' | 'csv' = 'json') => { |
152 | 186 | const _feedbacks = await exportAllFeedbacks(localStorage.token, selectedModelId).catch( |
153 | 187 | (err) => { |
154 | 188 | toast.error(err); |
|
157 | 191 | ); |
158 | 192 |
|
159 | 193 | if (_feedbacks) { |
160 | | - let blob = new Blob([JSON.stringify(_feedbacks)], { |
161 | | - type: 'application/json' |
162 | | - }); |
163 | | - saveAs(blob, `feedback-history-export-${Date.now()}.json`); |
| 194 | + if (format === 'csv') { |
| 195 | + const csv = feedbacksToCsv(_feedbacks); |
| 196 | + let blob = new Blob([csv], { type: 'text/csv' }); |
| 197 | + saveAs(blob, `feedback-history-export-${Date.now()}.csv`); |
| 198 | + } else { |
| 199 | + let blob = new Blob([JSON.stringify(_feedbacks)], { |
| 200 | + type: 'application/json' |
| 201 | + }); |
| 202 | + saveAs(blob, `feedback-history-export-${Date.now()}.json`); |
| 203 | + } |
164 | 204 | } |
165 | 205 | }; |
166 | 206 |
|
|
190 | 230 |
|
191 | 231 | <div class="flex w-full justify-end gap-1.5"> |
192 | 232 | {#if total > 0} |
193 | | - <button |
194 | | - class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-200 transition" |
195 | | - on:click={() => { |
196 | | - exportHandler(); |
197 | | - }} |
198 | | - > |
199 | | - <div class="self-center font-medium line-clamp-1"> |
200 | | - {$i18n.t('Export')} |
| 233 | + <Dropdown align="end"> |
| 234 | + <button |
| 235 | + class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-200 transition" |
| 236 | + > |
| 237 | + <div class="self-center font-medium line-clamp-1"> |
| 238 | + {$i18n.t('Export')} |
| 239 | + </div> |
| 240 | + <ChevronDown className="size-3" strokeWidth="2.5" /> |
| 241 | + </button> |
| 242 | + |
| 243 | + <div slot="content"> |
| 244 | + <div |
| 245 | + class="w-[170px] rounded-2xl p-1 border border-gray-100 dark:border-gray-800 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg" |
| 246 | + > |
| 247 | + <button |
| 248 | + class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl" |
| 249 | + type="button" |
| 250 | + on:click={() => exportHandler('json')} |
| 251 | + > |
| 252 | + {$i18n.t('Export as JSON')} |
| 253 | + </button> |
| 254 | + |
| 255 | + <button |
| 256 | + class="select-none flex w-full gap-2 items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl" |
| 257 | + type="button" |
| 258 | + on:click={() => exportHandler('csv')} |
| 259 | + > |
| 260 | + {$i18n.t('Export as CSV')} |
| 261 | + </button> |
| 262 | + </div> |
201 | 263 | </div> |
202 | | - </button> |
| 264 | + </Dropdown> |
203 | 265 | {/if} |
204 | 266 | </div> |
205 | 267 | </div> |
|
0 commit comments