AgendaPro v1.0 - multi-tenant SaaS, mobile calendar, public booking
Multi-tenant scheduling SaaS (AgendaPro-equivalent): - Backend: Node + Express + node:sqlite, multi-tenant (admin/owner/employee), versioned migrations, 4 templates (estetica-spa, barberia, clinica, blank). - Frontend: React 18 + TS + Vite + Tailwind, FullCalendar drag-and-drop, Recharts dashboard, mobile-first (day/list views on mobile). - Features: calendar+services+employees+clients+tickets, dashboard with best employee/service, top tickets, top/frequent clients, commissions, cancellation policy + no-show tracking, public online booking (/b/:slug), cash register (cierre de caja), reminders/notifications center. - Verification: 65/65 e2e API, 58/58 visual (0 overflow, 0 console errors), typecheck clean, vite build OK.
This commit is contained in:
+171
@@ -0,0 +1,171 @@
|
||||
export type Role = "admin" | "owner" | "employee";
|
||||
export type AppointmentStatus = "scheduled" | "completed" | "cancelled" | "no_show";
|
||||
|
||||
export interface Business {
|
||||
id: number;
|
||||
name: string;
|
||||
industry: string;
|
||||
currency: string;
|
||||
currency_symbol: string;
|
||||
phone: string | null;
|
||||
address: string | null;
|
||||
slug?: string | null;
|
||||
plan?: string;
|
||||
status?: string;
|
||||
template?: string | null;
|
||||
trial_ends_at?: string | null;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
business_id: number | null;
|
||||
email: string;
|
||||
name: string;
|
||||
role: Role;
|
||||
employee_id: number | null;
|
||||
avatar_color: string;
|
||||
}
|
||||
|
||||
export interface Employee {
|
||||
id: number;
|
||||
business_id: number;
|
||||
name: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
color: string;
|
||||
role: string;
|
||||
active: 0 | 1;
|
||||
rating: number;
|
||||
hire_date: string | null;
|
||||
service_ids?: number[];
|
||||
stats?: EmployeeStats;
|
||||
}
|
||||
|
||||
export interface EmployeeStats {
|
||||
appointments_total: number;
|
||||
appointments_completed: number;
|
||||
revenue_total: number;
|
||||
avg_rating: number;
|
||||
utilization_pct: number;
|
||||
}
|
||||
|
||||
export interface Service {
|
||||
id: number;
|
||||
business_id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
category: string;
|
||||
duration_min: number;
|
||||
price: number;
|
||||
color: string;
|
||||
active: 0 | 1;
|
||||
employee_ids?: number[];
|
||||
}
|
||||
|
||||
export interface Client {
|
||||
id: number;
|
||||
business_id: number;
|
||||
name: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
notes: string | null;
|
||||
tags: string | null;
|
||||
created_at: string;
|
||||
stats?: ClientStats;
|
||||
}
|
||||
|
||||
export interface ClientStats {
|
||||
visits: number;
|
||||
total_spent: number;
|
||||
last_visit: string | null;
|
||||
avg_ticket: number;
|
||||
no_show_count: number;
|
||||
}
|
||||
|
||||
export interface Appointment {
|
||||
id: number;
|
||||
business_id: number;
|
||||
service_id: number;
|
||||
employee_id: number;
|
||||
client_id: number;
|
||||
start_at: string;
|
||||
end_at: string;
|
||||
status: AppointmentStatus;
|
||||
price: number;
|
||||
notes: string | null;
|
||||
created_by_user_id: number | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
// joined fields
|
||||
service?: Service;
|
||||
employee?: Pick<Employee, "id" | "name" | "color" | "role">;
|
||||
client?: Pick<Client, "id" | "name" | "phone" | "email" | "tags">;
|
||||
}
|
||||
|
||||
export interface Ticket {
|
||||
id: number;
|
||||
business_id: number;
|
||||
appointment_id: number | null;
|
||||
client_id: number;
|
||||
employee_id: number;
|
||||
service_id: number;
|
||||
amount: number;
|
||||
tip: number;
|
||||
payment_method: string;
|
||||
created_at: string;
|
||||
service?: Pick<Service, "id" | "name" | "category" | "color">;
|
||||
employee?: Pick<Employee, "id" | "name" | "color">;
|
||||
client?: Pick<Client, "id" | "name">;
|
||||
}
|
||||
|
||||
export interface DashboardOverview {
|
||||
revenue_today: number;
|
||||
revenue_week: number;
|
||||
revenue_month: number;
|
||||
revenue_change_pct: number;
|
||||
appts_today: number;
|
||||
appts_week: number;
|
||||
appts_upcoming: number;
|
||||
active_clients: number;
|
||||
avg_ticket: number;
|
||||
cancel_rate: number;
|
||||
occupancy_pct: number;
|
||||
}
|
||||
|
||||
export interface RankedEmployee {
|
||||
employee: Pick<Employee, "id" | "name" | "color" | "role">;
|
||||
appointments: number;
|
||||
revenue: number;
|
||||
avg_rating: number;
|
||||
utilization_pct: number;
|
||||
}
|
||||
|
||||
export interface RankedService {
|
||||
service: Pick<Service, "id" | "name" | "category" | "color">;
|
||||
count: number;
|
||||
revenue: number;
|
||||
}
|
||||
|
||||
export interface RankedClient {
|
||||
client: Pick<Client, "id" | "name" | "tags" | "phone">;
|
||||
visits: number;
|
||||
total_spent: number;
|
||||
last_visit: string | null;
|
||||
}
|
||||
|
||||
export interface TopTicket {
|
||||
ticket: Ticket;
|
||||
}
|
||||
|
||||
export interface RevenuePoint {
|
||||
date: string;
|
||||
revenue: number;
|
||||
appts: number;
|
||||
}
|
||||
|
||||
export interface CategorySlice {
|
||||
category: string;
|
||||
revenue: number;
|
||||
count: number;
|
||||
}
|
||||
Reference in New Issue
Block a user