This commit is contained in:
+198
@@ -0,0 +1,198 @@
|
||||
use cot::db::{model, Auto, ForeignKey};
|
||||
|
||||
/// Lead status: new request from the website
|
||||
/// new -> in_progress -> converted | rejected
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum LeadStatus {
|
||||
New,
|
||||
InProgress,
|
||||
Converted,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
impl LeadStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::New => "new",
|
||||
Self::InProgress => "in_progress",
|
||||
Self::Converted => "converted",
|
||||
Self::Rejected => "rejected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Client status
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ClientStatus {
|
||||
Active,
|
||||
Archived,
|
||||
}
|
||||
|
||||
impl ClientStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Active => "active",
|
||||
Self::Archived => "archived",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit status
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum VisitStatus {
|
||||
Scheduled,
|
||||
Completed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl VisitStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Scheduled => "scheduled",
|
||||
Self::Completed => "completed",
|
||||
Self::Cancelled => "cancelled",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Media file type
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MediaType {
|
||||
Photo,
|
||||
Video,
|
||||
}
|
||||
|
||||
impl MediaType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Photo => "photo",
|
||||
Self::Video => "video",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// User (admin) status
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum UserStatus {
|
||||
Active,
|
||||
Archived,
|
||||
}
|
||||
|
||||
impl UserStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Active => "active",
|
||||
Self::Archived => "archived",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Models
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A lead submitted via the public website form.
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct Lead {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
pub name: String,
|
||||
pub phone: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub comment: Option<String>,
|
||||
/// new | in_progress | converted | rejected
|
||||
pub status: String,
|
||||
pub client_id: Option<ForeignKey<Client>>,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
/// A confirmed client created from a lead (or manually).
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct Client {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
pub name: String,
|
||||
pub phone: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub address: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
/// Unique token for the public media page (client views photos/videos here).
|
||||
#[model(unique)]
|
||||
pub media_token: String,
|
||||
/// Hex color for calendar display, e.g. "#7c6ed4"
|
||||
pub color: Option<String>,
|
||||
/// active | archived
|
||||
pub status: String,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
/// A scheduled pet-sitting visit.
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct Visit {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
pub client_id: ForeignKey<Client>,
|
||||
pub user_id: ForeignKey<User>,
|
||||
pub visit_date: chrono::NaiveDate,
|
||||
pub time_start: String,
|
||||
pub time_end: String,
|
||||
pub notes: Option<String>,
|
||||
/// Public notes visible to client on their portal.
|
||||
pub public_notes: Option<String>,
|
||||
/// Feedback text from client via portal.
|
||||
pub client_feedback: Option<String>,
|
||||
/// scheduled | completed | cancelled
|
||||
pub status: String,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
/// A photo or video uploaded for a client (optionally tied to a visit).
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct Media {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
pub client_id: ForeignKey<Client>,
|
||||
pub visit_id: Option<ForeignKey<Visit>>,
|
||||
pub file_path: String,
|
||||
/// photo | video
|
||||
pub file_type: String,
|
||||
pub caption: Option<String>,
|
||||
/// active | archived
|
||||
pub status: String,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
/// An admin user who can log in to manage the system.
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct User {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
#[model(unique)]
|
||||
pub login: String,
|
||||
pub password_hash: String,
|
||||
pub display_name: Option<String>,
|
||||
/// active | archived
|
||||
pub status: String,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
/// Global key-value settings (telegram_bot_token, telegram_chat_id, etc.).
|
||||
#[derive(Debug, Clone)]
|
||||
#[model]
|
||||
pub struct Setting {
|
||||
#[model(primary_key)]
|
||||
pub id: Auto<i64>,
|
||||
#[model(unique)]
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
Reference in New Issue
Block a user