refactor habit creation form to support both creation and editing

This commit is contained in:
2026-03-05 06:04:32 -05:00
parent 77d47cf4b0
commit 9363bbcc76
3 changed files with 75 additions and 63 deletions
+1 -1
View File
@@ -94,7 +94,7 @@
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="sm:max-w-lg"> <Dialog.Content class="sm:max-w-lg">
<HabitCreationForm <HabitCreationForm
onHabitCreated={() => { onComplete={() => {
showCreateDialog = false; showCreateDialog = false;
}} }}
/> />
+8 -26
View File
@@ -1,23 +1,21 @@
<script lang="ts"> <script lang="ts">
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js'; import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
import { Button, buttonVariants } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { EllipsisVertical, Pencil, Trash2 } from '@lucide/svelte'; import { EllipsisVertical, Pencil, Trash2 } from '@lucide/svelte';
import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js'; import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { Label } from '$lib/components/ui/label/index.js';
import { getHabitService } from '$lib/context/habits.svelte'; import { getHabitService } from '$lib/context/habits.svelte';
import HabitCreationForm from './HabitCreationForm.svelte';
const habitService = getHabitService(); const habitService = getHabitService();
let { habitId } = $props(); let { habitId }: { habitId: string } = $props();
// Get the current habit reactively from the service // Get the current habit reactively from the service
const currentHabit = $derived(habitService.habits.find(h => h.id === habitId)); const currentHabit = $derived(habitService.habits.find((h) => h.id === habitId));
let deleteHabitDialog = $state(false); let deleteHabitDialog = $state(false);
let editHabitDialog = $state(false); let editHabitDialog = $state(false);
</script> </script>
<DropdownMenu.Root> <DropdownMenu.Root>
@@ -47,26 +45,10 @@
<!-- Edit Habit Dialog --> <!-- Edit Habit Dialog -->
<Dialog.Root bind:open={editHabitDialog}> <Dialog.Root bind:open={editHabitDialog}>
<Dialog.Content class="sm:max-w-110"> <Dialog.Content class="sm:max-w-lg">
<Dialog.Header> {#if currentHabit}
<Dialog.Title>Edit profile</Dialog.Title> <HabitCreationForm habit={currentHabit} onComplete={() => { editHabitDialog = false; }} />
<Dialog.Description> {/if}
Make changes to your profile here. Click save when you're done.
</Dialog.Description>
</Dialog.Header>
<div class="grid gap-4 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<Label for="name" class="text-right">Name</Label>
<Input id="name" value="Pedro Duarte" class="col-span-3" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label for="username" class="text-right">Username</Label>
<Input id="username" value="@peduarte" class="col-span-3" />
</div>
</div>
<Dialog.Footer>
<Button type="submit">Save changes</Button>
</Dialog.Footer>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
@@ -7,23 +7,48 @@
import * as Collapsible from '$lib/components/ui/collapsible'; import * as Collapsible from '$lib/components/ui/collapsible';
import { ChevronDown, ChevronUp } from '@lucide/svelte'; import { ChevronDown, ChevronUp } from '@lucide/svelte';
import { getHabitService } from '$lib/context/habits.svelte'; import { getHabitService } from '$lib/context/habits.svelte';
import type { Habit } from '$lib/triplit/schema';
let { onHabitCreated }: { onHabitCreated?: () => Promise<void> | void } = $props(); let {
habit,
onComplete
}: {
habit?: Habit;
onComplete?: () => Promise<void> | void;
} = $props();
const habitService = getHabitService(); const habitService = getHabitService();
const isEditMode = $derived(!!habit);
let showAdvancedOptions = $state(false); let showAdvancedOptions = $state(false);
let isSubmitting = $state(false); let isSubmitting = $state(false);
// Form fields // Form fields - initialize from habit if editing
let name = $state(''); let name = $state(habit?.name ?? '');
let target = $state(1); let target = $state(habit?.target ?? 1);
let duration = $state(1); let duration = $state(habit?.duration ?? 1);
let unit = $state(''); let unit = $state(habit?.unit ?? '');
let increment = $state(1); let increment = $state(habit?.increment ?? 1);
let startDate = $state(new Date().toISOString().split('T')[0]); let startDate = $state(
let active = $state(true); habit?.startDate
? habit.startDate.toISOString().split('T')[0]
: new Date().toISOString().split('T')[0]
);
let active = $state(habit?.active ?? true);
// Re-initialize when habit changes (for edit mode)
$effect(() => {
if (habit) {
name = habit.name;
target = habit.target;
duration = habit.duration;
unit = habit.unit;
increment = habit.increment;
startDate = habit.startDate.toISOString().split('T')[0];
active = habit.active;
}
});
async function handleSubmit() { async function handleSubmit() {
if (isSubmitting || !name.trim()) return; if (isSubmitting || !name.trim()) return;
@@ -31,21 +56,24 @@
try { try {
isSubmitting = true; isSubmitting = true;
await habitService.createHabit({ const habitData = {
name, name,
target, target,
duration, duration,
increment, increment,
unit, unit,
startDate: (() => { startDate: (() => {
// Create date in local timezone to avoid UTC conversion issues
const [year, month, day] = startDate.split('-').map(Number); const [year, month, day] = startDate.split('-').map(Number);
return new Date(year, month - 1, day); return new Date(year, month - 1, day);
})(), })(),
active active
}) };
// Reset form if (isEditMode && habit) {
await habitService.updateHabit(habit.id, habitData);
} else {
await habitService.createHabit(habitData);
// Reset form only for create mode
name = ''; name = '';
target = 1; target = 1;
duration = 1; duration = 1;
@@ -54,11 +82,11 @@
startDate = new Date().toISOString().split('T')[0]; startDate = new Date().toISOString().split('T')[0];
active = true; active = true;
showAdvancedOptions = false; showAdvancedOptions = false;
}
// Notify parent onComplete?.();
onHabitCreated?.();
} catch (error) { } catch (error) {
console.error('Failed to create habit:', error); console.error(`Failed to ${isEditMode ? 'update' : 'create'} habit:`, error);
} finally { } finally {
isSubmitting = false; isSubmitting = false;
} }
@@ -66,9 +94,11 @@
</script> </script>
<Dialog.Header> <Dialog.Header>
<Dialog.Title>Create a new habit</Dialog.Title> <Dialog.Title>{isEditMode ? 'Edit habit' : 'Create a new habit'}</Dialog.Title>
<Dialog.Description> <Dialog.Description>
Add a new habit to track. Set your target and customize the tracking period. {isEditMode
? 'Update your habit settings. Click save when you\'re done.'
: 'Add a new habit to track. Set your target and customize the tracking period.'}
</Dialog.Description> </Dialog.Description>
</Dialog.Header> </Dialog.Header>
@@ -223,9 +253,9 @@
class="gap-2" class="gap-2"
> >
{#if isSubmitting} {#if isSubmitting}
Creating... {isEditMode ? 'Saving...' : 'Creating...'}
{:else} {:else}
Create Habit {isEditMode ? 'Save Changes' : 'Create Habit'}
{/if} {/if}
</Button> </Button>
</Dialog.Footer> </Dialog.Footer>