Reference

Conventions

LaunchDayOne specific patterns and utilities for consistent code organization.

Loading State Management

createWithLoading

Type-safe wrapper for async operations with loading state:

const isSubmitting = ref(false)
const withLoading = createWithLoading(isSubmitting)

await withLoading(async () => {
  await authClient.sendVerificationEmail({ email })
})

Location: /app/utils/withLoading.ts

createWithLoadingFor

Preserves function signatures while adding loading state:

const isLoading = ref(false)
const fetchUserWithLoading = createWithLoadingFor(isLoading, authClient.getUser)

// Maintains original args and return type
const user = await fetchUserWithLoading('123', options)

useWithLoading

Composable wrapper combining both patterns:

const { isLoading, fnWithLoading: sendVerificationEmail } = useWithLoading(_sendVerificationEmail)
await sendVerificationEmail('ada@example.com')

Location: /app/composables/useWithLoading.ts

Toast Messages

useToastMessage

Strongly typed notification system with preset colors/icons:

const { errorToast, successToast, infoToast } = useToastMessage()

successToast({
  title: 'Profile updated',
  description: 'Your changes have been saved',
})

errorToast({
  title: 'Update failed',
  description: error.message || 'Unable to update. Please try again.',
})

Location: /app/composables/useToastMessage.ts

Flash Messages (Server → Client)

Cookie-based pattern for server-to-client notifications:

// Server sets cookie
event.node.res.setHeader('Set-Cookie', serialize('flash_message__success', 'Account created!'))

// Client displays on mount
showFlashMessageFromCookie() // Auto-called in app.vue

Cookies: flash_message__error, flash_message__info, flash_message__success

Location: /app/utils/showFlashMessageFromCookie.ts

Form Submissions

Standard pattern combining loading, validation, and error handling:

<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type * as z from 'zod'

// Define form schema and state
const schema = z.object({ /* ... */ })
type Schema = z.output<typeof schema>

const state = reactive<Schema>({ /* ... */ })

// Separate handler for wrapping with useWithLoading
async function _onSubmit(event: FormSubmitEvent<Schema>) {}

// Wrap handler with loading state - provides isLoading ref + wrapped fn
const { isLoading, fnWithLoading: onSubmit } = useWithLoading(_onSubmit)
</script>

<template>
  <UForm
    :schema="schema"
    :state="state"
    class="space-y-6"
    @submit="onSubmit"
  >
    <!-- Form Fields -->

    <!-- Submit Button -->
    <UButton
      type="submit"
      size="lg"
      block
      :loading="isLoading"
    >
      Add User
    </UButton>
  </UForm>
</template>

Location: /app/components/page/admin/users/CreateUserModal.vue

Modal/Confirm Dialogs

useConfirm

Composable for confirmation dialogs:

const { confirm } = useConfirm({
  title: 'Deactivate Account?',
  body: 'Your account will be deactivated immediately. This action cannot be undone.',
  confirmBtnProps: { label: 'Deactivate', color: 'error' },
  onConfirm: async () => {
    await userStore.deactivateCurrentAccount()
  },
})

await confirm() // Non-dismissible during async operation

Location: /app/composables/useConfirm.ts

Copyright © 2025