145 lines
5.0 KiB
Markdown
145 lines
5.0 KiB
Markdown
# Data Models: WasaPrecruit MVP
|
|
|
|
This document describes the core data entities and their structures stored in the database (PostgreSQL) for the WasaPrecruit MVP.
|
|
|
|
**Notation:**
|
|
|
|
* Types follow TypeScript syntax.
|
|
* `PK`: Primary Key
|
|
* `FK`: Foreign Key
|
|
* `?`: Optional field
|
|
|
|
## 1. `Aspirant`
|
|
|
|
Represents a potential modeling aspirant who has interacted with the system via WhatsApp and potentially submitted the web form.
|
|
|
|
```typescript
|
|
interface Aspirant {
|
|
id: string; // PK - Generated UUID or unique identifier
|
|
whatsappId: string; // Unique - Aspirant's WhatsApp phone number (e.g., E.164 format)
|
|
whatsappName?: string; // Name provided by WhatsApp profile (can be unreliable)
|
|
|
|
// Data from Web Form
|
|
firstName?: string;
|
|
lastName?: string;
|
|
email?: string; // Optional, depending on form
|
|
location?: string; // e.g., City/Region
|
|
preferredModelingType?: string[]; // Array of strings (e.g., ['Runway', 'Commercial'])
|
|
hasKids?: boolean;
|
|
preferredShift?: 'morning' | 'afternoon' | 'evening' | 'flexible' | 'any';
|
|
unavailableShifts?: ('morning' | 'afternoon' | 'evening')[];
|
|
notes?: string; // Any additional notes from the form
|
|
formSubmittedAt?: Date;
|
|
|
|
// System Data
|
|
photoUrl?: string; // URL to the photo stored in S3 (submitted via WhatsApp)
|
|
conversationId?: string; // FK to Conversation (might be implicitly linked via whatsappId)
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
```
|
|
|
|
**Indexes:**
|
|
|
|
* `whatsappId` (Unique)
|
|
* `createdAt`
|
|
* `formSubmittedAt`
|
|
|
|
## 2. `Conversation`
|
|
|
|
Represents a chat conversation between the system/recruiter and an aspirant.
|
|
|
|
```typescript
|
|
interface Conversation {
|
|
id: string; // PK - Generated UUID
|
|
aspirantWhatsappId: string; // FK (conceptually) to Aspirant via whatsappId - identifies the participant
|
|
// OR alternatively: aspirantId: string; // FK to Aspirant.id if preferred
|
|
|
|
status: 'new' | 'open' | 'pending_form' | 'pending_photo' | 'needs_attention' | 'closed' | 'archived'; // Conversation status
|
|
assignedRecruiterId?: string; // FK to Recruiter/User (if assignment is implemented later)
|
|
lastMessageTimestamp?: Date; // Timestamp of the last message for sorting
|
|
unreadCount: number; // For recruiter view
|
|
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
```
|
|
|
|
**Indexes:**
|
|
|
|
* `aspirantWhatsappId`
|
|
* `status`
|
|
* `lastMessageTimestamp`
|
|
* `assignedRecruiterId` (if used)
|
|
|
|
**Note:** A conversation might be implicitly defined by messages associated with a unique `aspirantWhatsappId`. A separate `Conversation` table is useful for managing state (`status`, `assignedRecruiterId`, `unreadCount`) independent of individual messages.
|
|
|
|
## 3. `Message`
|
|
|
|
Represents a single message within a conversation.
|
|
|
|
```typescript
|
|
interface Message {
|
|
id: string; // PK - Generated UUID or provider's message ID (if unique and suitable)
|
|
conversationId: string; // FK to Conversation
|
|
senderType: 'aspirant' | 'recruiter' | 'bot';
|
|
senderId?: string; // FK to Recruiter/User ID if senderType is 'recruiter'
|
|
aspirantWhatsappId: string; // Denormalized for easier querying by aspirant
|
|
|
|
text?: string; // Message content (if text)
|
|
mediaUrl?: string; // URL to media (e.g., photo in S3)
|
|
mediaType?: 'image' | 'video' | 'audio' | 'document'; // Type of media
|
|
|
|
providerMessageId?: string; // Original message ID from WhatsApp provider (e.g., Twilio SID)
|
|
status: 'sent' | 'delivered' | 'read' | 'failed'; // Status of outbound messages
|
|
timestamp: Date; // Time the message was sent/received
|
|
|
|
createdAt: Date;
|
|
}
|
|
```
|
|
|
|
**Indexes:**
|
|
|
|
* `conversationId`
|
|
* `aspirantWhatsappId`
|
|
* `timestamp`
|
|
* `senderType`
|
|
|
|
## 4. `Recruiter` (User Model)
|
|
|
|
Represents an agency recruiter using the platform (details depend on authentication provider like Cognito).
|
|
|
|
```typescript
|
|
interface Recruiter {
|
|
id: string; // PK - Often corresponds to Cognito User Sub
|
|
name: string;
|
|
email: string; // Unique
|
|
// Add roles/permissions if needed later
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
```
|
|
|
|
**Indexes:**
|
|
|
|
* `email` (Unique)
|
|
|
|
## Relationships
|
|
|
|
* One `Aspirant` potentially relates to one `Conversation` (based on `whatsappId`).
|
|
* One `Conversation` contains many `Messages`.
|
|
* One `Recruiter` can send many `Messages`.
|
|
|
|
## Notes on Evolution
|
|
|
|
* **Normalization:** Some fields like `aspirantWhatsappId` are denormalized onto `Message` for query performance. Assess trade-offs if data consistency becomes complex.
|
|
* **JSONB:** Consider using PostgreSQL's JSONB type for flexible fields like `formDetails` if the web form structure is expected to change frequently, though explicit columns offer better indexing and type safety initially.
|
|
* **Scalability:** Partitioning message tables by date or `conversationId` might be necessary for very high volumes in the future.
|
|
|
|
## Change Log
|
|
|
|
| Change | Date | Version | Description | Author |
|
|
| ------------- | ---------- | ------- | ------------- | -------------- |
|
|
| Initial draft | YYYY-MM-DD | 0.1 | Initial draft | {Agent/Person} |
|
|
| ... | ... | ... | ... | ... |
|