refactor habit creation form to support both creation and editing
This commit is contained in:
@@ -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;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user