Initial commit: nick docs
This commit is contained in:
134
02 - Data Models/RequestTemplate.md
Normal file
134
02 - Data Models/RequestTemplate.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: RequestTemplate
|
||||
tags: [data-model, mongoose]
|
||||
aliases: [Template, Request Template, IRequestTemplate]
|
||||
---
|
||||
|
||||
# RequestTemplate
|
||||
|
||||
A reusable template authored by a seller. When a buyer visits the template's `shareableLink`, the front-end pre-fills a new [[PurchaseRequest]] with the template's category, urgency, specs, delivery info, and an optional default seller `proposal`. The schema mirrors `PurchaseRequest` for fast cloning, plus template-specific bookkeeping (`isActive`, `usageCount`, `maxUsage`, `expiresAt`).
|
||||
|
||||
> [!note] Source
|
||||
> `backend/src/models/RequestTemplate.ts:65` — schema definition
|
||||
> `backend/src/models/RequestTemplate.ts:295` — model export
|
||||
|
||||
## Schema
|
||||
|
||||
| Field | Type | Required | Default | Validation | Index | Description |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| `sellerId` | ObjectId → [[User]] | yes | — | — | yes (compound) | Template author. |
|
||||
| `title` | String | yes | — | trim, maxlength 200 | — | Headline. |
|
||||
| `description` | String | yes | — | trim, maxlength 2000 | — | Description. |
|
||||
| `categoryId` | ObjectId → [[Category]] | yes | — | — | yes (compound) | Category. |
|
||||
| `productType` | String | no | `physical_product` | enum: `physical_product` / `digital_product` / `service` / `consultation` | yes (compound) | Fulfilment type. |
|
||||
| `productLink` | String | no | — | URL regex | — | Reference URL. |
|
||||
| `size` | String | no | — | trim, maxlength 100 | — | Size. |
|
||||
| `color` | String | no | — | trim, maxlength 100 | — | Color. |
|
||||
| `brand` | String | no | — | trim, maxlength 100 | — | Brand. |
|
||||
| `quantity` | Number | no | `1` | min 1 | — | Default unit count. |
|
||||
| `budget.min` | Number | no | — | min 0 | — | Lower bound. |
|
||||
| `budget.max` | Number | no | — | min 0 | — | Upper bound. |
|
||||
| `budget.currency` | String | no | `USD` | enum: `USD` / `EUR` / `IRR` | — | Currency. |
|
||||
| `urgency` | String | no | `medium` | enum: `low` / `medium` / `high` / `urgent` | — | Urgency. |
|
||||
| `tags[]` | String[] | no | — | trim | — | Tags. |
|
||||
| `specifications[].key` | String | yes | — | trim | — | Spec key. |
|
||||
| `specifications[].value` | String | yes | — | trim | — | Spec value. |
|
||||
| `specifications[].label` | String | no | — | trim | — | Human label. |
|
||||
| `deliveryInfo.deliveryType` | String | no | `physical` | enum: `physical` / `online` | — | Delivery channel. |
|
||||
| `deliveryInfo.notes` | String | no | — | — | — | Notes. |
|
||||
| `deliveryInfo.email` | String | no | — | email regex | — | Digital delivery email. |
|
||||
| `serviceInfo.duration` | Number | no | — | min 0.5 | — | Hours. |
|
||||
| `serviceInfo.sessionType` | String | no | — | enum: `online` / `in_person` / `hybrid` | — | Session type. |
|
||||
| `serviceInfo.location` | String | no | — | trim, maxlength 200 | — | Location. |
|
||||
| `serviceInfo.requirements[]` | String[] | no | — | trim | — | Pre-requisites. |
|
||||
| `proposal.title` | String | no | — | trim, maxlength 200 | — | Default offer title. |
|
||||
| `proposal.price` | Number | no | — | min 0.01 | — | Default offer price. |
|
||||
| `proposal.deliveryTime` | Number | no | — | min 1, max 365 | — | Default ETA in days. |
|
||||
| `proposal.description` | String | no | — | trim, maxlength 1000 | — | Default offer description. |
|
||||
| `attachments[]` | String[] | no | — | — | — | File URLs. |
|
||||
| `images[]` | String[] | no | — | trim | — | Image URLs. |
|
||||
| `metadata.source` | String | no | `manual` | enum: `manual` / `template` / `api` | — | Origin. |
|
||||
| `metadata.templateId` | String | no | — | trim | — | Originating template id. |
|
||||
| `metadata.version` | String | no | — | trim | — | Schema version. |
|
||||
| `isActive` | Boolean | no | `true` | — | yes (single + compound) | Active flag. |
|
||||
| `shareableLink` | String | yes | — | trim | unique (+ compound) | Public link slug. |
|
||||
| `usageCount` | Number | no | `0` | min 0 | — | Number of times used. |
|
||||
| `maxUsage` | Number | no | `null` | min 1 | — | Optional cap. |
|
||||
| `expiresAt` | Date | no | `null` | — | yes | Optional expiry. |
|
||||
| `createdAt` | Date | auto | — | — | yes (desc) | Mongoose timestamp. |
|
||||
| `updatedAt` | Date | auto | — | — | — | Mongoose timestamp. |
|
||||
|
||||
## Virtuals
|
||||
|
||||
None defined.
|
||||
|
||||
## Indexes
|
||||
|
||||
Defined at `backend/src/models/RequestTemplate.ts:283-293`:
|
||||
|
||||
- `{ categoryId: 1 }`
|
||||
- `{ productType: 1 }`
|
||||
- `{ isActive: 1 }`
|
||||
- `{ createdAt: -1 }`
|
||||
- `{ expiresAt: 1 }`
|
||||
- `{ sellerId: 1, isActive: 1 }`
|
||||
- `{ shareableLink: 1, isActive: 1 }`
|
||||
- `{ productType: 1, isActive: 1 }`
|
||||
- `{ categoryId: 1, productType: 1 }`
|
||||
|
||||
`shareableLink` and `sellerId` already get indexes from `unique: true` / field-level conventions (see source comment at line 282).
|
||||
|
||||
## Pre/Post Hooks
|
||||
|
||||
None declared.
|
||||
|
||||
## Instance Methods
|
||||
|
||||
None defined.
|
||||
|
||||
## Static Methods
|
||||
|
||||
None defined.
|
||||
|
||||
## Relationships
|
||||
|
||||
- **References**: [[User]] (`sellerId`), [[Category]] (`categoryId`).
|
||||
- **Referenced by**: [[PurchaseRequest]] (`metadata.templateId` as string), [[Review]] (`subjectId` when `subjectType === 'template'`).
|
||||
|
||||
## State Transitions
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> active : created
|
||||
active --> inactive : seller toggles off
|
||||
inactive --> active : seller toggles on
|
||||
active --> expired : expiresAt passed
|
||||
active --> capped : usageCount == maxUsage
|
||||
expired --> [*]
|
||||
capped --> [*]
|
||||
```
|
||||
|
||||
> [!note] Soft state
|
||||
> Only `isActive` is persisted directly. `expired` and `capped` are derived at query time using `expiresAt` and `usageCount`.
|
||||
|
||||
## Common Queries
|
||||
|
||||
```ts
|
||||
// Seller's active templates
|
||||
RequestTemplate.find({ sellerId, isActive: true }).sort({ createdAt: -1 });
|
||||
|
||||
// Public template by slug
|
||||
RequestTemplate.findOne({ shareableLink: slug, isActive: true });
|
||||
|
||||
// Bump usage atomically
|
||||
RequestTemplate.findOneAndUpdate(
|
||||
{ _id, isActive: true, $or: [{ maxUsage: null }, { $expr: { $lt: ['$usageCount', '$maxUsage'] } }] },
|
||||
{ $inc: { usageCount: 1 } },
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
// Cleanup expired
|
||||
RequestTemplate.find({ expiresAt: { $lt: new Date() }, isActive: true });
|
||||
```
|
||||
|
||||
Related: [[PurchaseRequest]], [[User]], [[Category]], [[Review]].
|
||||
Reference in New Issue
Block a user