odac 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.github/workflows/auto-pr-description.yml +3 -1
  2. package/CHANGELOG.md +127 -0
  3. package/README.md +39 -36
  4. package/bin/odac.js +1 -31
  5. package/client/odac.js +871 -994
  6. package/docs/backend/01-overview/03-development-server.md +7 -7
  7. package/docs/backend/02-structure/01-typical-project-layout.md +1 -0
  8. package/docs/backend/03-config/00-configuration-overview.md +9 -0
  9. package/docs/backend/03-config/01-database-connection.md +1 -1
  10. package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
  11. package/docs/backend/04-routing/09-websocket.md +29 -0
  12. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
  13. package/docs/backend/05-controllers/03-controller-classes.md +27 -41
  14. package/docs/backend/05-forms/01-custom-forms.md +103 -95
  15. package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
  16. package/docs/backend/07-views/02-rendering-a-view.md +1 -1
  17. package/docs/backend/07-views/03-variables.md +5 -5
  18. package/docs/backend/07-views/04-request-data.md +1 -1
  19. package/docs/backend/07-views/08-backend-javascript.md +1 -1
  20. package/docs/backend/08-database/01-getting-started.md +100 -0
  21. package/docs/backend/08-database/02-basics.md +136 -0
  22. package/docs/backend/08-database/03-advanced.md +84 -0
  23. package/docs/backend/08-database/04-migrations.md +48 -0
  24. package/docs/backend/09-validation/01-the-validator-service.md +1 -0
  25. package/docs/backend/10-authentication/03-register.md +8 -1
  26. package/docs/backend/10-authentication/04-odac-register-forms.md +46 -46
  27. package/docs/backend/10-authentication/05-session-management.md +1 -1
  28. package/docs/backend/10-authentication/06-odac-login-forms.md +48 -48
  29. package/docs/backend/10-authentication/07-magic-links.md +134 -0
  30. package/docs/backend/11-mail/01-the-mail-service.md +118 -28
  31. package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
  32. package/docs/backend/13-utilities/01-odac-var.md +7 -7
  33. package/docs/backend/13-utilities/02-ipc.md +73 -0
  34. package/docs/frontend/01-overview/01-introduction.md +5 -1
  35. package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
  36. package/docs/index.json +16 -124
  37. package/eslint.config.mjs +5 -47
  38. package/package.json +9 -4
  39. package/src/Auth.js +362 -104
  40. package/src/Config.js +7 -2
  41. package/src/Database.js +188 -0
  42. package/src/Ipc.js +330 -0
  43. package/src/Mail.js +408 -37
  44. package/src/Odac.js +65 -9
  45. package/src/Request.js +70 -48
  46. package/src/Route/Cron.js +4 -1
  47. package/src/Route/Internal.js +214 -11
  48. package/src/Route/Middleware.js +7 -2
  49. package/src/Route.js +106 -26
  50. package/src/Server.js +80 -11
  51. package/src/Storage.js +165 -0
  52. package/src/Validator.js +94 -2
  53. package/src/View/Form.js +193 -17
  54. package/src/View.js +46 -1
  55. package/src/WebSocket.js +18 -3
  56. package/template/config.json +1 -1
  57. package/template/route/www.js +12 -10
  58. package/test/core/{Candy.test.js → Odac.test.js} +2 -2
  59. package/docs/backend/08-database/01-database-connection.md +0 -99
  60. package/docs/backend/08-database/02-using-mysql.md +0 -322
  61. package/src/Mysql.js +0 -575
@@ -1,6 +1,6 @@
1
1
  ## 🚀 Development Server
2
2
 
3
- Odac provides a built-in development server that allows you to test your website locally without running the full Odac server infrastructure.
3
+ ODAC provides a built-in development server that allows you to test your website locally without running the full ODAC server infrastructure.
4
4
 
5
5
  ### Quick Start
6
6
 
@@ -10,7 +10,7 @@ Navigate to your website directory and run one of these commands:
10
10
  # Using npm script
11
11
  npm start
12
12
 
13
- # Using Odac directly
13
+ # Using ODAC directly
14
14
  odac framework run
15
15
  ```
16
16
 
@@ -24,7 +24,7 @@ You can specify a custom port by adding it as an argument:
24
24
  # Using npm script with custom port
25
25
  npm start 8080
26
26
 
27
- # Using Odac directly with custom port
27
+ # Using ODAC directly with custom port
28
28
  odac framework run 8080
29
29
  ```
30
30
 
@@ -41,13 +41,13 @@ The development server (`npm start`) is designed for:
41
41
 
42
42
  **Important**: The development server does NOT provide DNS, SSL, or other production services.
43
43
 
44
- For production deployment with full Odac server features, create your website using:
44
+ For production deployment with full ODAC server features, create your website using:
45
45
 
46
46
  ```bash
47
47
  odac web create
48
48
  ```
49
49
 
50
- This registers your website with the Odac server and provides:
50
+ This registers your website with the ODAC server and provides:
51
51
 
52
52
  - **Automatic SSL** certificate management
53
53
  - **DNS handling** for your domain
@@ -57,7 +57,7 @@ This registers your website with the Odac server and provides:
57
57
 
58
58
  ### Package.json Scripts
59
59
 
60
- When you create a new website, Odac automatically generates a `package.json` with these useful scripts:
60
+ When you create a new website, ODAC automatically generates a `package.json` with these useful scripts:
61
61
 
62
62
  ```json
63
63
  {
@@ -76,4 +76,4 @@ When you create a new website, Odac automatically generates a `package.json` wit
76
76
  - The development server automatically detects changes in your code
77
77
  - Use `Ctrl+C` to stop the development server
78
78
  - The server will show helpful error messages in the console
79
- - All Odac framework features are available in development mode
79
+ - All ODAC framework features are available in development mode
@@ -10,6 +10,7 @@ Let's take a look at a typical project layout:
10
10
  - `route/`: This folder holds all your route definitions. The filename of the route file corresponds to the subdomain it serves.
11
11
  - `www.js`: Used for routes on your main domain (e.g., `www.example.com` or `example.com`).
12
12
  - `api.js`: Used for routes on a subdomain (e.g., `api.example.com`). When a request comes in for `api.example.com`, this is the route file that gets used. You can create files for any subdomain!
13
+ - `class/`: A dedicated place for your business logic classes and reusable services (e.g., `User.js`, `Mailer.js`).
13
14
  - `controller/`: This is where the magic happens! Controllers contain the main logic for your application.
14
15
  - `page/`: We suggest putting controllers that show HTML pages in here.
15
16
  - `api/`: If you're building an API, it's a great idea to keep those controllers separate in their own folder.
@@ -161,6 +161,15 @@ const nodeEnv = process.env.NODE_ENV
161
161
  }
162
162
  ```
163
163
 
164
+ **Debug Mode:**
165
+ Enable verbose logging for development. This helps in troubleshooting by inspecting detailed logs for system actions like sending emails (e.g., SMTP connection details, server responses). Default is `false`.
166
+
167
+ ```json
168
+ {
169
+ "debug": true
170
+ }
171
+ ```
172
+
164
173
  Early Hints is a performance optimization feature that works automatically without any configuration. The server sends preliminary HTTP headers to the browser before the final response, allowing browsers to start preloading critical resources (CSS, JavaScript, fonts) earlier. This is completely zero-config - it detects resources from your HTML automatically and sends hints on subsequent requests.
165
174
 
166
175
  See individual documentation sections for detailed configuration options.
@@ -15,7 +15,7 @@ When you add a `mysql` object to your `config.json`, the system will automatical
15
15
  }
16
16
  ```
17
17
 
18
- Once this is configured, you can directly use `Odac.Mysql` commands to run queries.
18
+ Once this is configured, you can directly use `Odac.DB` commands to run queries.
19
19
 
20
20
  ### Using Environment Variables
21
21
 
@@ -3,18 +3,24 @@
3
3
  For simple pages that don't require complex logic in a controller, you can render a view directly from your route file by passing a view configuration object as the second parameter.
4
4
 
5
5
  #### `page(path, { ... })`
6
- This defines a page and immediately tells it which view components to render.
6
+ This defines a page and immediately tells it which view components to render. You can also pass variables directly in the object, which will be available in your views.
7
7
 
8
8
  ```javascript
9
9
  Odac.Route.page("/users", {
10
+ // View Configuration
10
11
  skeleton: "dashboard",
11
12
  header: "dashboard.main",
12
13
  sidebar: "dashboard.main",
13
14
  footer: "dashboard.main",
14
- content: "users"
15
+ content: "users",
16
+
17
+ // Page Variables
18
+ title: "User Management",
19
+ description: "Manage your platform users here"
15
20
  });
16
21
  ```
17
- This example tells Odac to render the `/users` page by assembling a view from multiple parts, likely using a main `dashboard` skeleton and filling it with different content blocks.
22
+
23
+ This example tells Odac to render the `/users` page using the `dashboard` skeleton. Additionally, `title` and `description` are set as variables and can be accessed in your views using `<odac var="title" />`.
18
24
 
19
25
  **Page Identifier:** When using view objects, the page identifier (accessible via `Odac.page()` in frontend) is automatically set to the `content` or `all` value. In this example, the page identifier would be `"users"`, allowing you to run page-specific JavaScript:
20
26
 
@@ -45,6 +45,35 @@ web/
45
45
  │ └── websocket.js # WebSocket routes (recommended)
46
46
  ```
47
47
 
48
+ ### Using Controllers
49
+
50
+ You can also specify a connector file as a string. Odac will look for the file in `controller/ws/`.
51
+
52
+ ```javascript
53
+ // route/websocket.js
54
+ Odac.Route.ws('/chat', 'ChatController')
55
+ ```
56
+
57
+ **File Structure:**
58
+ ```
59
+ web/
60
+ ├── controller/
61
+ │ └── ws/
62
+ │ └── ChatController.js
63
+ ```
64
+
65
+ **Controller File:**
66
+ ```javascript
67
+ // controller/ws/ChatController.js
68
+ module.exports = Odac => {
69
+ Odac.ws.send({type: 'welcome'})
70
+
71
+ Odac.ws.on('message', data => {
72
+ // ...
73
+ })
74
+ }
75
+ ```
76
+
48
77
  ## WebSocket Client API (Odac.ws)
49
78
 
50
79
  The WebSocket client is accessible via `Odac.ws` in your handler, providing a consistent API pattern with HTTP routes.
@@ -14,6 +14,8 @@ Remember the `Odac` object? It's your best friend inside a controller. It's pass
14
14
 
15
15
  * `Odac.return(data)`: Send back a response.
16
16
  * `Odac.direct(url)`: Redirect the user to a new page.
17
+ * `Odac.set(key, value)`: Pass variables to your View template.
18
+ * `Odac.share(key, value)`: Share data directly with frontend JavaScript (`odac.data()`).
17
19
  * `Odac.cookie(key, value)`: Set a browser cookie.
18
20
  * `Odac.validator()`: Check user input easily.
19
21
  * `Odac.setInterval(callback, delay)`: Schedule repeating tasks (auto-cleanup).
@@ -1,13 +1,15 @@
1
- ## 🎓 Controller Classes
1
+ ## 🧩 Service Classes
2
2
 
3
- While simple function exports work great for basic controllers, you can also organize your code using classes. This is especially useful when you want to share logic between multiple methods or keep related functionality together.
3
+ You can organize your business logic into reusable classes. This is especially useful when you want to share logic between multiple methods or keep related functionality together.
4
4
 
5
- #### Creating a Controller Class
5
+ We recommend placing these files in the `class/` directory.
6
6
 
7
- A controller class receives the `Odac` object in its constructor, giving you access to all services throughout your class methods:
7
+ #### Creating a Service Class
8
+
9
+ Any class file placed in the `class/` directory will be automatically loaded and available on the `Odac` object. The class receives the global `Odac` request instance in its constructor, giving you access to all request-scoped services:
8
10
 
9
11
  ```javascript
10
- // controller/User.js
12
+ // class/User.js
11
13
  class User {
12
14
  constructor(Odac) {
13
15
  this.Odac = Odac
@@ -15,10 +17,10 @@ class User {
15
17
 
16
18
  async getProfile() {
17
19
  const user = await this.Odac.Auth.user()
18
- return this.Odac.return({
20
+ return {
19
21
  success: true,
20
22
  user: user
21
- })
23
+ }
22
24
  }
23
25
 
24
26
  async updateProfile() {
@@ -34,60 +36,44 @@ class User {
34
36
  const email = await this.Odac.request('email')
35
37
 
36
38
  // Update user in database
37
- await this.Odac.Mysql.query('UPDATE users SET name = ?, email = ? WHERE id = ?', [
38
- name,
39
- email,
40
- this.Odac.Auth.user().id
41
- ])
39
+ await this.Odac.DB.users.where('id', this.Odac.Auth.user().id).update({
40
+ name: name,
41
+ email: email
42
+ })
42
43
 
43
- return this.Odac.return({
44
+ return {
44
45
  success: true,
45
46
  message: 'Profile updated successfully'
46
- })
47
+ }
47
48
  }
48
49
  }
49
50
 
50
51
  module.exports = User
51
52
  ```
52
53
 
53
- #### Using Controller Classes in Routes
54
+ #### Naming Collisions
54
55
 
55
- Once you've created a controller class, you can use it in your routes just like any other controller:
56
+ If your class name conflicts with a built-in Odac service (like `Mail`, `DB`, `Auth`), it will be automatically placed under `Odac.App` namespace to prevent errors.
56
57
 
57
- ```javascript
58
- // route/www.js
59
- Odac.Route.buff = 'www'
58
+ Example: `class/Mail.js` (conflicts with core Mail) -> `Odac.App.Mail`
60
59
 
61
- // Access class methods using dot notation
62
- Odac.Route.get('/profile', 'User.getProfile')
63
- Odac.Route.post('/profile/update', 'User.updateProfile')
64
- ```
65
-
66
- #### Accessing Classes in Controllers
60
+ #### Accessing Services in Controllers
67
61
 
68
- Controller classes are automatically instantiated for each request and attached to the `Odac` object. You can access them from any controller:
62
+ Service classes are automatically instantiated for each request and attached to the `Odac` object using the file name. You can access them from any controller:
69
63
 
70
64
  ```javascript
65
+ // controller/get/profile.js
71
66
  module.exports = async function (Odac) {
72
- // Access your User class
67
+ // Access your User class via Odac.User
73
68
  const profile = await Odac.User.getProfile()
74
69
 
75
70
  return Odac.return(profile)
76
71
  }
77
72
  ```
78
73
 
79
- #### Benefits of Controller Classes
80
-
81
- - **Organization**: Group related methods together
82
- - **Reusability**: Share logic between different routes
83
- - **Maintainability**: Easier to manage complex controllers
84
- - **Context**: The `Odac` object is always available via `this.Odac`
85
-
86
- #### Class vs Function Controllers
87
-
88
- Both approaches work perfectly fine. Use what makes sense for your project:
89
-
90
- - **Functions**: Great for simple, single-purpose controllers
91
- - **Classes**: Better for complex logic with multiple related methods
74
+ #### Benefits of Service Classes
92
75
 
93
- The framework automatically detects whether your export is a class or a function and handles it accordingly.
76
+ - **Organization**: Group related business logic together in `class/`
77
+ - **Reusability**: Share logic between different controllers and routes
78
+ - **Context**: The `Odac` object is injected, providing access to `Auth`, `DB`, `Request`, etc.
79
+ - **Separation of Concerns**: Keep your Controllers lightweight by moving heavy logic to Services.
@@ -5,10 +5,10 @@ Odac provides an automatic form system with built-in validation, CSRF protection
5
5
  ## Basic Usage
6
6
 
7
7
  ```html
8
- <odac:form action="/contact/submit" method="POST">
9
- <odac:field name="email" type="email" label="Email">
8
+ <odac:form action="Contact.submit" method="POST">
9
+ <odac:input name="email" type="email" label="Email">
10
10
  <odac:validate rule="required|email" message="Valid email required"/>
11
- </odac:field>
11
+ </odac:input>
12
12
 
13
13
  <odac:submit text="Send" loading="Sending..."/>
14
14
  </odac:form>
@@ -18,21 +18,23 @@ Odac provides an automatic form system with built-in validation, CSRF protection
18
18
 
19
19
  ### `<odac:form>`
20
20
 
21
- - `action` - Form submission URL (optional if using `table`)
21
+ - `action` - Controller action `Controller.method` (optional if using `table`)
22
22
  - `method` - HTTP method (default: POST)
23
23
  - `table` - Database table name for automatic insert (optional)
24
24
  - `redirect` - Redirect URL after success (optional)
25
25
  - `success` - Success message (optional)
26
26
  - `class` - Additional CSS classes
27
27
  - `id` - Form ID attribute
28
+ - `clear` - Set to `false` to disable auto-clearing inputs on success (optional)
28
29
 
29
30
  ```html
30
- <!-- With custom controller -->
31
- <odac:form action="/api/save" method="POST" class="my-form" id="contact-form">
31
+ <!-- With custom controller action -->
32
+ <!-- Executes 'submit' method in 'contact' controller -->
33
+ <odac:form action="Contact.submit" method="POST" class="my-form" id="contact-form">
32
34
  <!-- fields here -->
33
35
  </odac:form>
34
36
 
35
- <!-- With automatic DB insert -->
37
+ <!-- With automatic DB insert (no controller needed) -->
36
38
  <odac:form table="waitlist" redirect="/" success="Thank you for joining!">
37
39
  <!-- fields here -->
38
40
  </odac:form>
@@ -40,69 +42,69 @@ Odac provides an automatic form system with built-in validation, CSRF protection
40
42
 
41
43
  ## Field Types
42
44
 
43
- ### `<odac:field>`
45
+ ### `<odac:input>`
44
46
 
45
47
  Supports all standard HTML input types:
46
48
 
47
49
  ```html
48
50
  <!-- Text input with multiple validations -->
49
- <odac:field name="username" type="text" label="Username" placeholder="Enter username">
51
+ <odac:input name="username" type="text" label="Username" placeholder="Enter username">
50
52
  <odac:validate rule="required" message="Username is required"/>
51
53
  <odac:validate rule="minlen:3" message="Username must be at least 3 characters"/>
52
54
  <odac:validate rule="maxlen:20" message="Username cannot exceed 20 characters"/>
53
55
  <odac:validate rule="alphanumeric" message="Username can only contain letters and numbers"/>
54
- </odac:field>
56
+ </odac:input>
55
57
 
56
58
  <!-- Email input -->
57
- <odac:field name="email" type="email" label="Email Address" placeholder="your@email.com">
59
+ <odac:input name="email" type="email" label="Email Address" placeholder="your@email.com">
58
60
  <odac:validate rule="required" message="Email address is required"/>
59
61
  <odac:validate rule="email" message="Please enter a valid email address"/>
60
62
  <odac:validate rule="maxlen:100" message="Email is too long"/>
61
- </odac:field>
63
+ </odac:input>
62
64
 
63
65
  <!-- Password input with strong validation -->
64
- <odac:field name="password" type="password" label="Password">
66
+ <odac:input name="password" type="password" label="Password">
65
67
  <odac:validate rule="required" message="Password is required"/>
66
68
  <odac:validate rule="minlen:8" message="Password must be at least 8 characters long"/>
67
69
  <odac:validate rule="maxlen:50" message="Password is too long"/>
68
- </odac:field>
70
+ </odac:input>
69
71
 
70
72
  <!-- Textarea with character limits -->
71
- <odac:field name="message" type="textarea" label="Your Message" placeholder="Tell us what you think...">
73
+ <odac:input name="message" type="textarea" label="Your Message" placeholder="Tell us what you think...">
72
74
  <odac:validate rule="required" message="Please enter your message"/>
73
75
  <odac:validate rule="minlen:10" message="Message must be at least 10 characters"/>
74
76
  <odac:validate rule="maxlen:500" message="Message cannot exceed 500 characters"/>
75
- </odac:field>
77
+ </odac:input>
76
78
 
77
79
  <!-- Checkbox for terms acceptance -->
78
- <odac:field name="agree" type="checkbox" label="I agree to the Terms of Service and Privacy Policy">
80
+ <odac:input name="agree" type="checkbox" label="I agree to the Terms of Service and Privacy Policy">
79
81
  <odac:validate rule="accepted" message="You must accept the terms to continue"/>
80
- </odac:field>
82
+ </odac:input>
81
83
 
82
84
  <!-- Number input with range -->
83
- <odac:field name="age" type="number" label="Your Age">
85
+ <odac:input name="age" type="number" label="Your Age">
84
86
  <odac:validate rule="required" message="Age is required"/>
85
87
  <odac:validate rule="min:18" message="You must be at least 18 years old"/>
86
88
  <odac:validate rule="max:120" message="Please enter a valid age"/>
87
- </odac:field>
89
+ </odac:input>
88
90
 
89
91
  <!-- Phone number -->
90
- <odac:field name="phone" type="text" label="Phone Number" placeholder="+1 (555) 123-4567">
92
+ <odac:input name="phone" type="text" label="Phone Number" placeholder="+1 (555) 123-4567">
91
93
  <odac:validate rule="required" message="Phone number is required"/>
92
94
  <odac:validate rule="minlen:10" message="Phone number must be at least 10 digits"/>
93
- </odac:field>
95
+ </odac:input>
94
96
 
95
97
  <!-- URL input -->
96
- <odac:field name="website" type="url" label="Website" placeholder="https://example.com">
98
+ <odac:input name="website" type="url" label="Website" placeholder="https://example.com">
97
99
  <odac:validate rule="url" message="Please enter a valid URL"/>
98
- </odac:field>
100
+ </odac:input>
99
101
 
100
102
  <!-- Name with alpha validation -->
101
- <odac:field name="full_name" type="text" label="Full Name" placeholder="John Doe">
103
+ <odac:input name="full_name" type="text" label="Full Name" placeholder="John Doe">
102
104
  <odac:validate rule="required" message="Full name is required"/>
103
105
  <odac:validate rule="minlen:2" message="Name must be at least 2 characters"/>
104
106
  <odac:validate rule="maxlen:50" message="Name is too long"/>
105
- </odac:field>
107
+ </odac:input>
106
108
  ```
107
109
 
108
110
  ### Field Attributes
@@ -121,9 +123,9 @@ Supports all standard HTML input types:
121
123
  Add validation rules to fields:
122
124
 
123
125
  ```html
124
- <odac:field name="username" type="text">
126
+ <odac:input name="username" type="text">
125
127
  <odac:validate rule="required|minlen:3|maxlen:20" message="Username must be 3-20 characters"/>
126
- </odac:field>
128
+ </odac:input>
127
129
  ```
128
130
 
129
131
  ### Available Rules
@@ -202,59 +204,64 @@ Automatically set field values without user input:
202
204
  <odac:submit text="Save" loading="Saving..." class="btn btn-primary" id="save-btn"/>
203
205
  ```
204
206
 
205
- ## Controller Handler
207
+ ## Controller Handler (Server Actions)
208
+
209
+ Handle form submission directly in your controller. The action is defined as `ControllerName.methodName`.
206
210
 
207
- Handle form submission in your controller:
211
+ **View:**
212
+ ```html
213
+ <odac:form action="Contact.submit">
214
+ ...
215
+ </odac:form>
216
+ ```
217
+
218
+ **Controller (controller/Contact.js):**
208
219
 
209
220
  ```javascript
210
- module.exports = {
211
- submit: Odac => {
212
- // Access validated form data
213
- const data = Odac.formData
214
-
215
- // data contains all field values
216
- console.log(data.email, data.message)
217
-
218
- // Process the data (save to database, send email, etc.)
219
-
220
- // Return success response
221
- return Odac.return({
222
- result: {
223
- success: true,
224
- message: 'Form submitted successfully!',
225
- redirect: '/thank-you' // Optional redirect
226
- }
227
- })
221
+ module.exports = class Contact {
222
+ constructor(Odac) {
223
+ this.Odac = Odac
224
+ }
225
+
226
+ async submit(form) {
227
+ // 1. Access validated clean data
228
+ const { email, message } = form.data
229
+
230
+ // 2. Perform your logic
231
+ // await this.Odac.Mail().send(email, message)
232
+
233
+ // 3. Return success easily
234
+ return form.success('Message sent successfully!', '/thank-you')
228
235
  }
229
236
  }
230
237
  ```
231
238
 
232
239
  ### Error Handling
233
240
 
234
- Return validation errors:
241
+ Return errors using the helper method:
235
242
 
236
243
  ```javascript
237
- module.exports = {
238
- submit: Odac => {
239
- const data = Odac.formData
244
+ module.exports = class Contact {
245
+ constructor(Odac) {
246
+ this.Odac = Odac
247
+ }
248
+
249
+ async submit(form) {
250
+ const { email } = form.data
240
251
 
241
- // Custom validation
242
- if (data.email.includes('spam')) {
243
- return Odac.return({
244
- result: {success: false},
245
- errors: {
246
- email: 'This email is not allowed'
247
- }
248
- })
252
+ // Custom backend validation
253
+ if (email.includes('spam')) {
254
+ // Returns field-specific error
255
+ return form.error('email', 'Spam is not allowed!')
249
256
  }
250
257
 
251
- return Odac.return({
252
- result: {success: true, message: 'Success!'}
253
- })
258
+ return form.success('Success!')
254
259
  }
255
260
  }
256
261
  ```
257
262
 
263
+ **Note:** No route definition is needed for the action! The form system handles the routing securely.
264
+
258
265
  ## Automatic Database Insert
259
266
 
260
267
  Forms can automatically insert data into database without writing a controller:
@@ -263,13 +270,13 @@ Forms can automatically insert data into database without writing a controller:
263
270
 
264
271
  ```html
265
272
  <odac:form table="waitlist" redirect="/" success="Thank you for joining!">
266
- <odac:field name="email" type="email" label="Email">
273
+ <odac:input name="email" type="email" label="Email">
267
274
  <odac:validate rule="required|email|unique" message="Valid email required"/>
268
- </odac:field>
275
+ </odac:input>
269
276
 
270
- <odac:field name="name" type="text" label="Name">
277
+ <odac:input name="name" type="text" label="Name">
271
278
  <odac:validate rule="required|minlen:2" message="Name required"/>
272
- </odac:field>
279
+ </odac:input>
273
280
 
274
281
  <odac:set name="created_at" compute="now"/>
275
282
  <odac:set name="ip" compute="ip"/>
@@ -312,22 +319,22 @@ That's it! No controller needed. The form will:
312
319
  <div class="contact-page">
313
320
  <h1>Contact Us</h1>
314
321
 
315
- <odac:form action="/contact/submit" method="POST" class="contact-form">
316
- <odac:field name="name" type="text" label="Your Name" placeholder="Enter your name">
322
+ <odac:form action="Contact.submit" method="POST" class="contact-form">
323
+ <odac:input name="name" type="text" label="Your Name" placeholder="Enter your name">
317
324
  <odac:validate rule="required|minlen:3" message="Name must be at least 3 characters"/>
318
- </odac:field>
325
+ </odac:input>
319
326
 
320
- <odac:field name="email" type="email" label="Email" placeholder="your@email.com">
327
+ <odac:input name="email" type="email" label="Email" placeholder="your@email.com">
321
328
  <odac:validate rule="required|email" message="Please enter a valid email"/>
322
- </odac:field>
329
+ </odac:input>
323
330
 
324
- <odac:field name="subject" type="text" label="Subject" placeholder="What is this about?">
331
+ <odac:input name="subject" type="text" label="Subject" placeholder="What is this about?">
325
332
  <odac:validate rule="required|minlen:5" message="Subject must be at least 5 characters"/>
326
- </odac:field>
333
+ </odac:input>
327
334
 
328
- <odac:field name="message" type="textarea" label="Message" placeholder="Your message...">
335
+ <odac:input name="message" type="textarea" label="Message" placeholder="Your message...">
329
336
  <odac:validate rule="required|minlen:10" message="Message must be at least 10 characters"/>
330
- </odac:field>
337
+ </odac:input>
331
338
 
332
339
  <odac:submit text="Send Message" loading="Sending..." class="btn btn-primary"/>
333
340
  </odac:form>
@@ -337,29 +344,29 @@ That's it! No controller needed. The form will:
337
344
  ### Controller (controller/contact.js)
338
345
 
339
346
  ```javascript
340
- module.exports = {
341
- index: Odac => {
342
- Odac.View.skeleton('default')
343
- Odac.View.set({content: 'contact'})
344
- Odac.View.print()
345
- },
346
-
347
- submit: Odac => {
348
- const data = Odac.formData
347
+ module.exports = class Contact {
348
+ constructor(Odac) {
349
+ this.Odac = Odac
350
+ }
351
+
352
+ async index() {
353
+ this.Odac.View.skeleton('default')
354
+ this.Odac.View.set({content: 'contact'})
355
+ this.Odac.View.print()
356
+ }
357
+
358
+ async submit(form) {
359
+ // Get validated data from form helper
360
+ const { name, email, subject, message } = form.data
349
361
 
350
362
  // Save to database
351
- // await Odac.Mysql.query('INSERT INTO contacts SET ?', data)
363
+ // Save to database
364
+ // await this.Odac.DB.contacts.insert({ name, email, subject, message })
352
365
 
353
366
  // Send email notification
354
- // await Odac.Mail().to('admin@example.com').subject('New Contact').send(data.message)
367
+ // await this.Odac.Mail().to('admin@example.com').subject('New Contact').send(message)
355
368
 
356
- return Odac.return({
357
- result: {
358
- success: true,
359
- message: 'Thank you! We will get back to you soon.',
360
- redirect: '/'
361
- }
362
- })
369
+ return form.success('Thank you! We will get back to you soon.', '/')
363
370
  }
364
371
  }
365
372
  ```
@@ -368,7 +375,7 @@ module.exports = {
368
375
 
369
376
  ```javascript
370
377
  Odac.Route.page('/contact', 'contact')
371
- Odac.Route.post('/contact/submit', 'contact.submit')
378
+ // Note: No route needed for contact.submit action!
372
379
  ```
373
380
 
374
381
  ## Features
@@ -380,6 +387,7 @@ Odac.Route.post('/contact/submit', 'contact.submit')
380
387
  - **Loading States** - Automatic button state management
381
388
  - **Error Display** - Automatic error message rendering
382
389
  - **Success Messages** - Built-in success message handling
390
+ - **Auto-Clear** - Clears inputs on success (disable with `clear="false"`)
383
391
  - **Redirect Support** - Optional redirect after successful submission
384
392
 
385
393
  ## Security