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
+3 -3
View File
@@ -93,10 +93,10 @@
</Button>
</Dialog.Trigger>
<Dialog.Content class="sm:max-w-lg">
<HabitCreationForm
onHabitCreated={() => {
<HabitCreationForm
onComplete={() => {
showCreateDialog = false;
}}
}}
/>
</Dialog.Content>
</Dialog.Root>
+9 -27
View File
@@ -1,23 +1,21 @@
<script lang="ts">
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 * as AlertDialog from '$lib/components/ui/alert-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 HabitCreationForm from './HabitCreationForm.svelte';
const habitService = getHabitService();
let { habitId } = $props();
let { habitId }: { habitId: string } = $props();
// 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 editHabitDialog = $state(false);
</script>
<DropdownMenu.Root>
@@ -47,26 +45,10 @@
<!-- Edit Habit Dialog -->
<Dialog.Root bind:open={editHabitDialog}>
<Dialog.Content class="sm:max-w-110">
<Dialog.Header>
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
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 class="sm:max-w-lg">
{#if currentHabit}
<HabitCreationForm habit={currentHabit} onComplete={() => { editHabitDialog = false; }} />
{/if}
</Dialog.Content>
</Dialog.Root>
@@ -7,58 +7,86 @@
import * as Collapsible from '$lib/components/ui/collapsible';
import { ChevronDown, ChevronUp } from '@lucide/svelte';
import { getHabitService } from '$lib/context/habits.svelte';
import type { Habit } from '$lib/triplit/schema';
let {
habit,
onComplete
}: {
habit?: Habit;
onComplete?: () => Promise<void> | void;
} = $props();
let { onHabitCreated }: { onHabitCreated?: () => Promise<void> | void } = $props();
const habitService = getHabitService();
const isEditMode = $derived(!!habit);
let showAdvancedOptions = $state(false);
let isSubmitting = $state(false);
// Form fields
let name = $state('');
let target = $state(1);
let duration = $state(1);
let unit = $state('');
let increment = $state(1);
let startDate = $state(new Date().toISOString().split('T')[0]);
let active = $state(true);
// Form fields - initialize from habit if editing
let name = $state(habit?.name ?? '');
let target = $state(habit?.target ?? 1);
let duration = $state(habit?.duration ?? 1);
let unit = $state(habit?.unit ?? '');
let increment = $state(habit?.increment ?? 1);
let startDate = $state(
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() {
if (isSubmitting || !name.trim()) return;
try {
isSubmitting = true;
await habitService.createHabit({
const habitData = {
name,
target,
duration,
increment,
unit,
startDate: (() => {
// Create date in local timezone to avoid UTC conversion issues
const [year, month, day] = startDate.split('-').map(Number);
return new Date(year, month - 1, day);
})(),
active
})
};
// Reset form
name = '';
target = 1;
duration = 1;
unit = '';
increment = 1;
startDate = new Date().toISOString().split('T')[0];
active = true;
showAdvancedOptions = false;
// Notify parent
onHabitCreated?.();
if (isEditMode && habit) {
await habitService.updateHabit(habit.id, habitData);
} else {
await habitService.createHabit(habitData);
// Reset form only for create mode
name = '';
target = 1;
duration = 1;
unit = '';
increment = 1;
startDate = new Date().toISOString().split('T')[0];
active = true;
showAdvancedOptions = false;
}
onComplete?.();
} catch (error) {
console.error('Failed to create habit:', error);
console.error(`Failed to ${isEditMode ? 'update' : 'create'} habit:`, error);
} finally {
isSubmitting = false;
}
@@ -66,9 +94,11 @@
</script>
<Dialog.Header>
<Dialog.Title>Create a new habit</Dialog.Title>
<Dialog.Title>{isEditMode ? 'Edit habit' : 'Create a new habit'}</Dialog.Title>
<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.Header>
@@ -217,15 +247,15 @@
Cancel
</Button>
</Dialog.Close>
<Button
<Button
onclick={handleSubmit}
disabled={isSubmitting || !name.trim()}
class="gap-2"
>
{#if isSubmitting}
Creating...
{isEditMode ? 'Saving...' : 'Creating...'}
{:else}
Create Habit
{isEditMode ? 'Save Changes' : 'Create Habit'}
{/if}
</Button>
</Dialog.Footer>