ImportTasksModal.svelte•4.09 kB
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Button } from '$lib/components/ui/button';
import { Textarea } from '$lib/components/ui/textarea';
export let open = false;
const dispatch = createEventDispatcher();
const chatbotPrompt = `Please output a list of tasks in the following JSON format. Each task must have a \"title\" and an \"effort\" (\"low\", \"medium\", or \"high\"). Optionally, you can include a \"description\".\n\nExample:\n[\n { \"title\": \"Design login page\", \"effort\": \"medium\", \"description\": \"Create wireframes and UI for login\" },\n { \"title\": \"Implement authentication\", \"effort\": \"high\" }\n]`;
let inputValue = '';
let errorMsg = '';
let previewTasks: { title: string; effort: string; description?: string }[] = [];
let copied = false;
function handlePreview() {
errorMsg = '';
previewTasks = [];
try {
const parsed = JSON.parse(inputValue);
if (!Array.isArray(parsed)) throw new Error('Input must be a JSON array.');
for (const t of parsed) {
if (!t.title || !t.effort) throw new Error('Each task must have a title and effort.');
if (!['low', 'medium', 'high'].includes(t.effort)) throw new Error('Effort must be "low", "medium", or "high".');
}
previewTasks = parsed;
} catch (e) {
errorMsg = e instanceof Error ? e.message : 'Invalid input.';
}
}
function handleImport() {
handlePreview();
if (errorMsg || previewTasks.length === 0) return;
dispatch('import', { tasks: previewTasks });
inputValue = '';
previewTasks = [];
errorMsg = '';
}
function handleCancel() {
dispatch('cancel');
inputValue = '';
previewTasks = [];
errorMsg = '';
}
</script>
<Dialog.Root bind:open>
<Dialog.Content class="max-w-lg w-full">
<Dialog.Header>
<Dialog.Title>Import Tasks</Dialog.Title>
<Dialog.Description>
Paste a list of tasks generated by a chatbot, following the format below.
</Dialog.Description>
</Dialog.Header>
<div class="py-4 flex flex-col gap-4">
<div>
<Label for="chatbot-prompt">Chatbot Prompt</Label>
<div class="flex gap-2 mt-1">
<Textarea
id="chatbot-prompt"
class="w-full text-xs min-h-[40px] max-h-40"
rows={5}
readonly
value={chatbotPrompt}
/>
<Button
size="sm"
variant="secondary"
on:click={() => {
navigator.clipboard.writeText(chatbotPrompt);
copied = true;
setTimeout(() => copied = false, 1500);
}}
disabled={copied}
>
{copied ? 'Copied!' : 'Copy'}
</Button>
</div>
</div>
<div>
<Label for="import-tasks">JSON</Label>
<Textarea
id="import-tasks"
class="w-full min-h-[120px] max-h-96"
bind:value={inputValue}
placeholder="Paste JSON array of tasks here..."
/>
</div>
{#if errorMsg}
<div class="text-destructive text-sm">{errorMsg}</div>
{/if}
{#if previewTasks.length > 0}
<div class="bg-muted p-3 rounded text-sm">
<b>Preview:</b>
<ul class="list-disc ml-5 mt-2">
{#each previewTasks as t}
<li><b>{t.title}</b> ({t.effort}){t.description ? `: ${t.description}` : ''}</li>
{/each}
</ul>
</div>
{/if}
<Dialog.Footer class="flex justify-end gap-3 pt-2">
<Dialog.Close>
<Button variant="secondary" type="button" on:click={handleCancel}>Cancel</Button>
</Dialog.Close>
<Button variant="outline" type="button" on:click={handlePreview}>Preview</Button>
<Button variant="default" type="button" on:click={handleImport} disabled={!!errorMsg || !inputValue.trim()}>Import</Button>
</Dialog.Footer>
</div>
</Dialog.Content>
</Dialog.Root>