A comprehensive business management solution for Melody's nail salon, featuring visual appointment booking, dual-form sizing, inventory tracking, and portfolio showcase.
Perfect Finish is a full-stack Vue 3 application designed for nail tech business management. The system supports both client-facing booking (Turkish/English) and admin management interfaces, with emphasis on visual galleries, precise dual-form measurements, and loyalty programs.
Current Status: Development in progress Priority: Admin Section β Client Portal
- Framework: Vue 3.5 with TypeScript + Vite
- UI Library: shadcn-vue (reka-ui) + Tailwind CSS 4.1
- State Management: Pinia 3.0 with Options API pattern
- Data Fetching: Pinia Colada 0.17 (useQuery/useMutation)
- Routing: File-based routing (unplugin-vue-router)
- Validation: Vee-Validate + Zod
- Internationalization: Vue i18n (Turkish/English)
- Date Handling: date-fns (all date operations)
- Icons: Lucide Vue Next + Radix Icons
- BaaS: Supabase (Authentication + PostgreSQL + Storage)
- Database: PostgreSQL with Row Level Security (RLS)
- File Storage: Supabase Storage (multi-bucket strategy)
- Real-time: Supabase Realtime subscriptions
perfect-finish/
βββ src/
β βββ assets/ # Static assets and fonts
β βββ components/
β β βββ ui/ # shadcn-vue components
β β βββ admin/ # Admin-specific components
β β βββ client/ # Client portal components
β β βββ shared/ # Shared business components
β β βββ layout/ # Layout wrappers & sidebar
β β βββ icons/ # Custom icon components
β βββ composables/ # Vue composables (useQuery patterns)
β βββ i18n/ # Internationalization (en.json, tr.json)
β βββ lib/ # Core utilities
β β βββ pinia.ts # Pinia configuration
β β βββ supabase.ts # Supabase client
β β βββ utils.ts # Helper functions
β βββ pages/ # File-based routing
β β βββ admin/ # Admin dashboard pages
β β βββ (client)/ # Client portal (grouped route)
β β βββ auth/ # Authentication pages
β βββ stores/ # Pinia stores (Options API)
β βββ types/ # TypeScript definitions
β β βββ database.types.ts # Supabase generated types
β βββ main.ts
βββ docs/
β βββ project/ # Comprehensive documentation
βββ supabase/ # Supabase migration files
βββ CLAUDE.md # AI assistant context
- π¨ Visual Service Selection: Browse services with hero images and past work galleries
- π Smart Appointment Booking: 30-minute time slot system with availability checking
- πΌοΈ Inspiration Upload: Upload reference images or select from past work portfolio
- π€ Client Profile: Personal info, appointment history, loyalty dashboard
- π Loyalty Program: Points earning, frequency rewards, referral bonuses
- π± Mobile-First Design: Touch-friendly interface with WhatsApp integration
- π Business Dashboard: Today's appointments, revenue metrics, alerts
- π Calendar Management: Weekly/daily views with drag-drop scheduling
- π₯ Client Management: Searchable client list with detailed profiles
- β Dual Form Interface: Visual SVG hand selector for nail measurements
- πΌοΈ Gallery Manager: Upload service images, curate past work portfolio
- π¦ Inventory Tracking: Product variants, stock levels, usage tracking
- ποΈ Service Catalog: Services, add-ons, bundles with pricing
- π° Invoicing: Automated invoice generation with loyalty integration
- π Analytics: Revenue reports, service performance, client insights
- Visual Portfolio System: Service galleries and past work showcase for marketing
- Dual Form Measurements: Precise nail sizing recorded per hand via SVG interface
- Product Variants: Color-based inventory (e.g., Polygel: Nude, Pink, White)
- Flexible Pricing: Fixed, starting price, and variable pricing models
- Multi-Language: Full Turkish/English support with i18n
- WhatsApp Integration: Client communication and design negotiation
- Real-time Updates: Live appointment and inventory changes
- profiles: Users (clients, technicians, admins) with roles and preferences
- appointments: Service bookings with client, technician, services, and pricing
- services + addons: Service catalog with duration, pricing, and product links
- products + product_variants: Inventory with colors/sizes and stock tracking
- client_dual_forms: Nail measurements per hand (left/right) for each client
- invoices + invoice_items: Financial records with payment tracking
- loyalty_accounts + loyalty_transactions: Points system with audit log
- service_images + past_work_gallery: Visual portfolio and marketing content
PROFILES β book β APPOINTMENTS β generate β INVOICES β update β LOYALTY_ACCOUNTS
β β β β
DUAL_FORMS SERVICES + INVOICE_ITEMS LOYALTY_TRANSACTIONS
ADDONS PRODUCT_USAGE
β
SERVICE_IMAGES +
PAST_WORK_GALLERY
See Developer Documentation for complete entity definitions and relationships.
Browse Services (with galleries) β Select Service β Choose Time Slot β
Enter Client Info β Upload Inspiration β Submit Booking (pending) β
WhatsApp Negotiation β Admin Confirms β Appointment Confirmed
Dashboard β Today's Appointments β View Details β
Update Status (pending/confirmed/in_progress/completed) β
Upload Result Photos β Generate Invoice β Optional: Add to Gallery
Client Profile β Dual Forms Tab β Select Hand (Left/Right) β
Click Finger on SVG Hand β Select Dual Form Size β Save β
Auto-loaded for Future Appointments
Admin Dashboard β Gallery Manager β
Service Images: Upload & Categorize (hero/gallery/technique) β
Past Work: Bulk Upload Portfolio β Tag & Approve β
Set Featured Works β Publish to Client Portal
Products β Create/Select Product β Add Variants (colors/sizes) β
Set Stock Levels β Configure Usage Tracking β
Appointment Complete β Record Product Usage (optional) β
Auto-update Stock β Reorder Alerts
Appointment Completed β Generate Invoice β
Apply Loyalty Discounts β Record Payment β
Auto-calculate Loyalty Points β Update Client Balance β
Loyalty Transactions Log
See Workflow Documentation for detailed process flows and UI mockups.
- Node.js 18+ and npm
- Supabase account with project created
- Git for version control
1. Clone and Install
git clone <repository-url>
cd perfect-finish
npm install2. Environment Setup
# Create .env file
cp .env.example .env
# Add your Supabase credentials
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key3. Generate Database Types
npm run supabase:types4. Start Development Server
npm run dev # Start on localhost
npm run dev-host # Start with network access# Development
npm run dev # Start dev server (localhost)
npm run dev-host # Start dev server with host access
# Building
npm run build # Build for production (includes type-check)
npm run preview # Preview production build
# Code Quality
npm run lint # Run all linting (oxlint + eslint)
npm run lint:oxlint # Run oxlint with auto-fix
npm run lint:eslint # Run eslint with auto-fix
npm run format # Format code with Prettier
npm run type-check # TypeScript type checking
# Testing
npm run test:unit # Run unit tests (Vitest)
npm run test:e2e # Run E2E tests (Cypress)
npm run test:e2e:dev # Run Cypress in dev mode
# Database
npm run supabase:types # Generate TypeScript types from SupabaseOptions API Pattern for Stores:
// stores/auth.ts
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
profile: null as Profile | null,
}),
persist: {
key: 'auth-store',
pick: ['user', 'profile'], // Only persist specific fields
},
getters: {
isAuthenticated: (state) => !!state.user,
isAdmin: (state) => state.profile?.role === 'admin',
},
actions: {
async login(email: string, password: string) {
// Authentication logic
},
},
})Key Points:
- Use Options API syntax for all stores
- Configure persistence with
pinia-plugin-persistedstate - Stores manage UI state, NOT data fetching
- Keep stores focused and single-purpose
Query Pattern with Reactive Keys:
// composables/useAppointments.ts
import { useQuery, useMutation, useQueryCache } from '@pinia/colada'
// Fetch appointments with reactive parameters
export function useAppointments(dateRange: Ref<DateRange>) {
return useQuery({
key: () => ['appointments', dateRange.value], // Reactive key without computed()
query: async () => {
const { data, error } = await supabase
.from('appointments')
.select('*, client:profiles(*), service:services(*)')
.gte('appointment_date', format(dateRange.value.start, 'yyyy-MM-dd'))
.lte('appointment_date', format(dateRange.value.end, 'yyyy-MM-dd'))
if (error) throw error
return data || []
},
staleTime: 1000 * 60 * 5, // 5 minutes
})
}
// Create appointment mutation
export function useCreateAppointment() {
const queryCache = useQueryCache()
return useMutation({
mutation: async (data: CreateAppointmentData) => {
const { data: appointment, error } = await supabase
.from('appointments')
.insert(data)
.select()
.single()
if (error) throw error
return appointment
},
onSuccess: () => {
// Invalidate all appointment queries
queryCache.invalidateQueries({ key: ['appointments'] })
},
})
}Key Points:
- Use
useQueryfor data fetching with reactive parameters - Use
useMutationfor data modifications - Implement proper cache invalidation with
useQueryCache - Return
QueryReturn/MutationReturnfrom composables - No
computed()wrapper for reactive keys
Using <script setup> with TypeScript:
<script setup lang="ts">
import { useAppointments } from '@/composables/useAppointments'
import { useAppointmentStore } from '@/stores/appointments'
import { computed } from 'vue'
const appointmentStore = useAppointmentStore()
const dateRange = computed(() => appointmentStore.dateRange)
// Returns UseQueryReturn with data, isLoading, error
const { data: appointments, isLoading, error } = useAppointments(dateRange)
</script>
<template>
<div v-if="isLoading">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else>
<AppointmentCard v-for="apt in appointments" :key="apt.id" :appointment="apt" />
</div>
</template>Key Points:
- Use
<script setup>syntax consistently - Import shadcn-vue components from
@/components/ui/[component] - Follow existing component patterns
- Use
date-fnsfor all date operations - Proper TypeScript typing with database.types.ts
Always use date-fns for date operations:
import { format, isToday, isSameDay, startOfDay, endOfDay, parseISO } from 'date-fns'
// Format dates consistently
const formatDate = (date: string | Date) => {
const parsedDate = typeof date === 'string' ? parseISO(date) : date
return format(parsedDate, 'MMM dd, yyyy')
}
// Format time
const formatTime = (time: string) => {
return format(parseISO(`2000-01-01T${time}`), 'h:mm a')
}
// Check if appointment is today
const isAppointmentToday = (date: string) => {
return isToday(parseISO(date))
}Never use native Date methods - always use date-fns.
- Clean & Minimal: Light borders, efficient use of space, no heavy shadows
- Visual Hierarchy: Clear information architecture with proper spacing
- Productivity Focus: Fast, efficient admin tools with keyboard shortcuts
- Responsive Design: Mobile-first approach for all interfaces
- Accessibility: ARIA labels, keyboard navigation, screen reader support
/* Professional nail salon aesthetic */
--primary: 221 83% 53%; /* Blue - primary actions */
--primary-foreground: 0 0% 98%; /* White text on primary */
/* Neutral palette */
--background: 0 0% 100%; /* Pure white background */
--foreground: 222.2 84% 4.9%; /* Near-black text */
--muted: 210 40% 98%; /* Light gray backgrounds */
--border: 214.3 31.8% 91.4%; /* Subtle borders */
/* Semantic colors */
--success: 142 76% 36%; /* Green - completed */
--warning: 38 92% 50%; /* Amber - pending */
--destructive: 0 84% 60%; /* Red - cancelled */- Developer Documentation: Complete entity relationships, column definitions, and technical architecture
- Workflow Documentation: User workflows, UI patterns, and business processes
- CLAUDE.md: AI assistant context and coding guidelines
Dual Form System: Visual SVG hand interface for recording nail sizes per finger. Each client has up to 2 records (left/right hand). Critical for pre-appointment preparation.
Service Gallery System: Visual portfolio management with service images and past work showcase. Supports bulk upload for existing businesses.
Product Variants: Base products with multiple variants (colors/sizes). Example: Polygel β Nude, Pink, White variants with individual stock tracking.
Flexible Pricing: Services support fixed price, starting price (adjustable), and variable pricing models.
Loyalty System: Multiple reward types - points per dollar, frequency milestones, referral bonuses, friends booking discounts.
- Use
database.types.tsconsistently for all database entities - No
anytypes allowed - Proper Zod schemas for form validation
- Type-safe environment variables
- Single Responsibility Principle
- Consistent file naming (kebab-case for files, PascalCase for components)
- Barrel exports for clean imports (
index.tsfiles) - Proper component composition and reusability
- Virtual scrolling for large lists (@tanstack/vue-table)
- Image optimization with lazy loading
- Proper query caching with Pinia Colada
- v-memo for expensive renders
- Global error handler in main.ts
- Toast notifications for user feedback (vue-sonner)
- Graceful degradation for network issues
- Proper loading states and error boundaries
- Tailwind CSS 4.1 with shadcn-vue design system
- CSS custom properties for theming
- Mobile-first responsive design
- Clean, minimal aesthetic
npm run build # Build with type checking
npm run preview # Preview production buildVITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key- Generate latest database types
- Run type checking
- Run linting
- Test production build locally
- Configure Supabase RLS policies
- Set up environment variables
- Deploy to hosting platform (Vercel/Netlify)
- Test production deployment
- β Project setup with Vue 3 + TypeScript + Vite
- β Supabase integration and authentication
- β State management with Pinia
- β UI component library (shadcn-vue)
- β File-based routing
- β Database schema and types
- π Admin dashboard implementation
- π Appointment management system
- π Client management interface
- π Service catalog and gallery
- π Dual form interface (SVG hand)
- π Inventory management
- π Invoicing system
- π Loyalty program
- π Client portal (Turkish/English)
- π Analytics and reporting
This is a private project for Melody's nail salon business. For development inquiries, please contact the project maintainer.
Proprietary - All rights reserved
Built with β€οΈ for nail tech professionals