Skip to content

Commit 5a56c69

Browse files
Copilothotlong
andcommitted
Add examples and documentation for new field types
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent b6fdce5 commit 5a56c69

File tree

3 files changed

+511
-0
lines changed

3 files changed

+511
-0
lines changed

examples/modern-fields/README.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Modern Field Types Examples
2+
3+
This directory contains examples demonstrating the modern field types and cross-field validation capabilities in ObjectStack.
4+
5+
## New Field Types
6+
7+
### 1. Slider Field (`slider`)
8+
A numeric slider control for visual range selection with configurable steps and custom marks.
9+
10+
**Use Cases:**
11+
- Volume/brightness controls
12+
- Stock level indicators
13+
- Rating scales with visual feedback
14+
- Priority levels
15+
16+
**Configuration:**
17+
```typescript
18+
Field.slider({
19+
label: 'Stock Level',
20+
min: 0,
21+
max: 100,
22+
step: 1,
23+
defaultValue: 50,
24+
showValue: true,
25+
marks: {
26+
'0': 'Empty',
27+
'50': 'Medium',
28+
'100': 'Full',
29+
},
30+
})
31+
```
32+
33+
### 2. QR Code / Barcode Field (`qrcode`)
34+
Generate and scan QR codes and various barcode formats for product identification and tracking.
35+
36+
**Supported Formats:**
37+
- `qr` - QR Code
38+
- `ean13` - EAN-13 (European Article Number)
39+
- `ean8` - EAN-8
40+
- `code128` - Code 128
41+
- `code39` - Code 39
42+
- `upca` - UPC-A
43+
- `upce` - UPC-E
44+
45+
**Configuration:**
46+
```typescript
47+
// Product barcode
48+
Field.qrcode({
49+
label: 'Product Barcode',
50+
barcodeFormat: 'ean13',
51+
displayValue: true,
52+
allowScanning: true,
53+
unique: true,
54+
})
55+
56+
// QR code for URLs
57+
Field.qrcode({
58+
label: 'Product QR Code',
59+
barcodeFormat: 'qr',
60+
qrErrorCorrection: 'M', // L, M, Q, or H
61+
displayValue: false,
62+
})
63+
```
64+
65+
### 3. Geolocation Field (`geolocation`)
66+
GPS coordinates for location tracking with optional map display and geocoding.
67+
68+
**Configuration:**
69+
```typescript
70+
Field.geolocation({
71+
label: 'Warehouse Location',
72+
displayMap: true,
73+
allowGeocoding: true, // Convert addresses to coordinates
74+
})
75+
```
76+
77+
## Cross-Field Validation
78+
79+
Cross-field validation allows you to validate relationships between multiple fields, ensuring data integrity across your business logic.
80+
81+
### Basic Cross-Field Validation
82+
83+
```typescript
84+
{
85+
type: 'cross_field',
86+
name: 'end_after_start',
87+
condition: 'end_date > start_date',
88+
fields: ['start_date', 'end_date'],
89+
message: 'End date must be after start date',
90+
severity: 'error',
91+
}
92+
```
93+
94+
### Complex Cross-Field Validation
95+
96+
```typescript
97+
{
98+
type: 'cross_field',
99+
name: 'discount_limit',
100+
condition: 'discount_amount <= (ticket_price * 0.40)',
101+
fields: ['discount_amount', 'ticket_price'],
102+
message: 'Discount cannot exceed 40% of ticket price',
103+
severity: 'error',
104+
}
105+
```
106+
107+
### Conditional Validation
108+
109+
Apply validation rules only when certain conditions are met:
110+
111+
```typescript
112+
{
113+
type: 'conditional',
114+
name: 'published_requires_location',
115+
when: 'status = "published"',
116+
then: {
117+
type: 'script',
118+
name: 'venue_location_required',
119+
condition: 'venue_location = null',
120+
message: 'Venue location is required for published events',
121+
},
122+
}
123+
```
124+
125+
## Examples
126+
127+
### Product Object (`product.object.ts`)
128+
Demonstrates all new field types:
129+
- Slider for stock levels
130+
- QR codes for product identification
131+
- Geolocation for warehouse tracking
132+
- Color, rating, and address fields
133+
134+
### Event Object (`event.object.ts`)
135+
Demonstrates comprehensive cross-field validation:
136+
- Date range validation (end > start)
137+
- Capacity validation (attendees <= capacity)
138+
- Price validation (discount < price)
139+
- Conditional validation based on status
140+
- Warning thresholds
141+
142+
## Running Examples
143+
144+
To use these examples in your ObjectStack project:
145+
146+
1. Install dependencies:
147+
```bash
148+
pnpm install @objectstack/spec
149+
```
150+
151+
2. Import and use the objects:
152+
```typescript
153+
import { Product } from './src/product.object';
154+
import { Event } from './src/event.object';
155+
156+
// Use in your ObjectStack configuration
157+
export default {
158+
objects: [Product, Event],
159+
};
160+
```
161+
162+
## Salesforce Comparison
163+
164+
ObjectStack's cross-field validation is inspired by Salesforce validation rules but provides a more composable and type-safe approach:
165+
166+
**Salesforce:**
167+
```
168+
// Validation Rule: Close_Date_Must_Be_Future
169+
Error Condition Formula: CloseDate < TODAY()
170+
Error Message: Close Date must be in the future
171+
```
172+
173+
**ObjectStack:**
174+
```typescript
175+
{
176+
type: 'cross_field',
177+
name: 'close_date_future',
178+
condition: 'close_date > TODAY()',
179+
fields: ['close_date'],
180+
message: 'Close Date must be in the future',
181+
}
182+
```
183+
184+
## Additional Resources
185+
186+
- [ObjectStack Field Types Documentation](../../packages/spec/src/data/field.zod.ts)
187+
- [Validation Rules Documentation](../../packages/spec/src/data/validation.zod.ts)
188+
- [ObjectStack Protocol Guide](../../ARCHITECTURE.md)
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { ObjectSchema, Field } from '@objectstack/spec/data';
2+
import { ValidationRuleSchema } from '@objectstack/spec/data';
3+
4+
/**
5+
* Event Object - Demonstrates Cross-Field Validation
6+
*
7+
* This example shows cross-field validation capabilities:
8+
* - Date range validation (end_date > start_date)
9+
* - Capacity validation (attendees <= max_capacity)
10+
* - Price validation (discount < total_price)
11+
*/
12+
export const Event = ObjectSchema.create({
13+
name: 'event',
14+
label: 'Event',
15+
icon: 'calendar',
16+
nameField: 'title',
17+
enable: {
18+
apiEnabled: true,
19+
trackHistory: true,
20+
},
21+
fields: {
22+
title: Field.text({
23+
required: true,
24+
label: 'Event Title',
25+
maxLength: 200,
26+
}),
27+
28+
description: Field.richtext({
29+
label: 'Description',
30+
description: 'Event description with formatting',
31+
}),
32+
33+
// Date fields for cross-field validation
34+
start_date: Field.datetime({
35+
label: 'Start Date',
36+
required: true,
37+
}),
38+
39+
end_date: Field.datetime({
40+
label: 'End Date',
41+
required: true,
42+
}),
43+
44+
// Capacity fields for validation
45+
max_capacity: Field.number({
46+
label: 'Maximum Capacity',
47+
required: true,
48+
min: 1,
49+
}),
50+
51+
current_attendees: Field.number({
52+
label: 'Current Attendees',
53+
defaultValue: 0,
54+
min: 0,
55+
}),
56+
57+
// Pricing fields
58+
ticket_price: Field.currency({
59+
label: 'Ticket Price',
60+
required: true,
61+
min: 0,
62+
precision: 10,
63+
scale: 2,
64+
}),
65+
66+
discount_amount: Field.currency({
67+
label: 'Discount Amount',
68+
min: 0,
69+
precision: 10,
70+
scale: 2,
71+
}),
72+
73+
// Location
74+
venue_address: Field.address({
75+
label: 'Venue Address',
76+
required: true,
77+
addressFormat: 'us',
78+
}),
79+
80+
venue_location: Field.geolocation({
81+
label: 'Venue GPS Location',
82+
displayMap: true,
83+
allowGeocoding: true,
84+
}),
85+
86+
status: Field.select({
87+
label: 'Status',
88+
options: [
89+
{ label: 'Draft', value: 'draft', color: '#AAAAAA', default: true },
90+
{ label: 'Published', value: 'published', color: '#00AA00' },
91+
{ label: 'Cancelled', value: 'cancelled', color: '#AA0000' },
92+
{ label: 'Completed', value: 'completed', color: '#0000AA' },
93+
],
94+
}),
95+
},
96+
97+
// Cross-field validation rules
98+
validation: [
99+
// 1. End date must be after start date
100+
{
101+
type: 'cross_field',
102+
name: 'end_after_start',
103+
condition: 'end_date > start_date',
104+
fields: ['start_date', 'end_date'],
105+
message: 'End date must be after start date',
106+
severity: 'error',
107+
active: true,
108+
},
109+
110+
// 2. Current attendees cannot exceed max capacity
111+
{
112+
type: 'cross_field',
113+
name: 'attendees_within_capacity',
114+
condition: 'current_attendees <= max_capacity',
115+
fields: ['current_attendees', 'max_capacity'],
116+
message: 'Current attendees cannot exceed maximum capacity',
117+
severity: 'error',
118+
active: true,
119+
},
120+
121+
// 3. Discount cannot exceed ticket price
122+
{
123+
type: 'cross_field',
124+
name: 'discount_within_price',
125+
condition: 'discount_amount <= ticket_price',
126+
fields: ['discount_amount', 'ticket_price'],
127+
message: 'Discount amount cannot exceed ticket price',
128+
severity: 'error',
129+
active: true,
130+
},
131+
132+
// 4. Warn when approaching capacity
133+
{
134+
type: 'cross_field',
135+
name: 'approaching_capacity',
136+
condition: '(current_attendees / max_capacity) < 0.9',
137+
fields: ['current_attendees', 'max_capacity'],
138+
message: 'Event is approaching maximum capacity (90% full)',
139+
severity: 'warning',
140+
active: true,
141+
},
142+
143+
// 5. Event must be at least 1 hour long
144+
{
145+
type: 'cross_field',
146+
name: 'minimum_duration',
147+
condition: '(end_date - start_date) >= 3600', // 3600 seconds = 1 hour
148+
fields: ['start_date', 'end_date'],
149+
message: 'Event must be at least 1 hour in duration',
150+
severity: 'error',
151+
active: true,
152+
},
153+
154+
// 6. Conditional validation: Published events require venue location
155+
{
156+
type: 'conditional',
157+
name: 'published_requires_location',
158+
when: 'status = "published"',
159+
message: 'Published events must have venue location',
160+
then: {
161+
type: 'script',
162+
name: 'venue_location_required',
163+
condition: 'venue_location = null OR venue_location = ""',
164+
message: 'Venue location is required for published events',
165+
severity: 'error',
166+
active: true,
167+
},
168+
active: true,
169+
},
170+
],
171+
});
172+
173+
/**
174+
* Example Usage:
175+
*
176+
* // Valid event
177+
* const validEvent = {
178+
* title: 'Tech Conference 2024',
179+
* start_date: '2024-06-01T09:00:00Z',
180+
* end_date: '2024-06-01T17:00:00Z', // ✓ After start_date
181+
* max_capacity: 500,
182+
* current_attendees: 250, // ✓ Less than max_capacity
183+
* ticket_price: 100.00,
184+
* discount_amount: 20.00, // ✓ Less than ticket_price
185+
* status: 'published',
186+
* };
187+
*
188+
* // Invalid event - validation errors
189+
* const invalidEvent = {
190+
* title: 'Invalid Event',
191+
* start_date: '2024-06-01T09:00:00Z',
192+
* end_date: '2024-06-01T08:00:00Z', // ✗ Before start_date
193+
* max_capacity: 100,
194+
* current_attendees: 150, // ✗ Exceeds max_capacity
195+
* ticket_price: 50.00,
196+
* discount_amount: 75.00, // ✗ Exceeds ticket_price
197+
* };
198+
*/

0 commit comments

Comments
 (0)