hazo_notify 1.1.4 → 3.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.
- package/README.md +163 -41
- package/dist/components/template_manager/category_dialog.d.ts +11 -0
- package/dist/components/template_manager/category_dialog.d.ts.map +1 -0
- package/dist/components/template_manager/category_dialog.js +41 -0
- package/dist/components/template_manager/category_dialog.js.map +1 -0
- package/dist/components/template_manager/category_tree.d.ts +13 -0
- package/dist/components/template_manager/category_tree.d.ts.map +1 -0
- package/dist/components/template_manager/category_tree.js +45 -0
- package/dist/components/template_manager/category_tree.js.map +1 -0
- package/dist/components/template_manager/index.d.ts +10 -0
- package/dist/components/template_manager/index.d.ts.map +1 -0
- package/dist/components/template_manager/index.js +8 -0
- package/dist/components/template_manager/index.js.map +1 -0
- package/dist/components/template_manager/preview_dialog.d.ts +11 -0
- package/dist/components/template_manager/preview_dialog.d.ts.map +1 -0
- package/dist/components/template_manager/preview_dialog.js +15 -0
- package/dist/components/template_manager/preview_dialog.js.map +1 -0
- package/dist/components/template_manager/template_dialog.d.ts +12 -0
- package/dist/components/template_manager/template_dialog.d.ts.map +1 -0
- package/dist/components/template_manager/template_dialog.js +46 -0
- package/dist/components/template_manager/template_dialog.js.map +1 -0
- package/dist/components/template_manager/template_editor.d.ts +12 -0
- package/dist/components/template_manager/template_editor.d.ts.map +1 -0
- package/dist/components/template_manager/template_editor.js +57 -0
- package/dist/components/template_manager/template_editor.js.map +1 -0
- package/dist/components/template_manager/template_globals_admin.d.ts +8 -0
- package/dist/components/template_manager/template_globals_admin.d.ts.map +1 -0
- package/dist/components/template_manager/template_globals_admin.js +27 -0
- package/dist/components/template_manager/template_globals_admin.js.map +1 -0
- package/dist/components/template_manager/template_manager_admin.d.ts +44 -0
- package/dist/components/template_manager/template_manager_admin.d.ts.map +1 -0
- package/dist/components/template_manager/template_manager_admin.js +272 -0
- package/dist/components/template_manager/template_manager_admin.js.map +1 -0
- package/dist/lib/emailer/emailer.d.ts.map +1 -0
- package/dist/lib/emailer/emailer.js.map +1 -0
- package/dist/lib/emailer/index.d.ts.map +1 -0
- package/dist/lib/emailer/index.js.map +1 -0
- package/dist/lib/emailer/providers/index.d.ts.map +1 -0
- package/dist/lib/emailer/providers/index.js.map +1 -0
- package/dist/lib/emailer/providers/pop3_provider.d.ts.map +1 -0
- package/dist/lib/emailer/providers/pop3_provider.js.map +1 -0
- package/dist/lib/emailer/providers/smtp_provider.d.ts.map +1 -0
- package/dist/lib/emailer/providers/smtp_provider.js.map +1 -0
- package/dist/lib/emailer/providers/zeptomail_provider.d.ts.map +1 -0
- package/dist/{emailer → lib/emailer}/providers/zeptomail_provider.js +9 -6
- package/dist/lib/emailer/providers/zeptomail_provider.js.map +1 -0
- package/dist/lib/emailer/types.d.ts.map +1 -0
- package/dist/lib/emailer/types.js.map +1 -0
- package/dist/lib/emailer/utils/constants.d.ts.map +1 -0
- package/dist/lib/emailer/utils/constants.js.map +1 -0
- package/dist/lib/emailer/utils/index.d.ts.map +1 -0
- package/dist/lib/emailer/utils/index.js.map +1 -0
- package/dist/lib/emailer/utils/logger.d.ts.map +1 -0
- package/dist/lib/emailer/utils/logger.js.map +1 -0
- package/dist/lib/emailer/utils/validation.d.ts.map +1 -0
- package/dist/lib/emailer/utils/validation.js.map +1 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/template_manager/cache/scope_chain_cache.d.ts +26 -0
- package/dist/lib/template_manager/cache/scope_chain_cache.d.ts.map +1 -0
- package/dist/lib/template_manager/cache/scope_chain_cache.js +60 -0
- package/dist/lib/template_manager/cache/scope_chain_cache.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/config/config_loader.d.ts +16 -15
- package/dist/lib/template_manager/config/config_loader.d.ts.map +1 -0
- package/dist/lib/template_manager/config/config_loader.js +98 -0
- package/dist/lib/template_manager/config/config_loader.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/config/constants.d.ts +4 -54
- package/dist/lib/template_manager/config/constants.d.ts.map +1 -0
- package/dist/{template_manager → lib/template_manager}/config/constants.js +4 -84
- package/dist/lib/template_manager/config/constants.js.map +1 -0
- package/dist/lib/template_manager/config/index.d.ts.map +1 -0
- package/dist/lib/template_manager/config/index.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/db/category_repository.d.ts +22 -14
- package/dist/lib/template_manager/db/category_repository.d.ts.map +1 -0
- package/dist/{template_manager → lib/template_manager}/db/category_repository.js +127 -83
- package/dist/lib/template_manager/db/category_repository.js.map +1 -0
- package/dist/lib/template_manager/db/index.d.ts +6 -0
- package/dist/lib/template_manager/db/index.d.ts.map +1 -0
- package/dist/lib/template_manager/db/index.js +6 -0
- package/dist/lib/template_manager/db/index.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/db/template_repository.d.ts +25 -28
- package/dist/lib/template_manager/db/template_repository.d.ts.map +1 -0
- package/dist/lib/template_manager/db/template_repository.js +507 -0
- package/dist/lib/template_manager/db/template_repository.js.map +1 -0
- package/dist/lib/template_manager/engine/handlebars_engine.d.ts.map +1 -0
- package/dist/lib/template_manager/engine/handlebars_engine.js.map +1 -0
- package/dist/lib/template_manager/engine/index.d.ts.map +1 -0
- package/dist/lib/template_manager/engine/index.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/engine/variable_resolver.d.ts +13 -14
- package/dist/lib/template_manager/engine/variable_resolver.d.ts.map +1 -0
- package/dist/{template_manager → lib/template_manager}/engine/variable_resolver.js +25 -33
- package/dist/lib/template_manager/engine/variable_resolver.js.map +1 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.d.ts +20 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.js +46 -0
- package/dist/lib/template_manager/handlers/adapters/hazo_auth.js.map +1 -0
- package/dist/lib/template_manager/handlers/auth.d.ts +47 -0
- package/dist/lib/template_manager/handlers/auth.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/auth.js +67 -0
- package/dist/lib/template_manager/handlers/auth.js.map +1 -0
- package/dist/lib/template_manager/handlers/index.d.ts +67 -0
- package/dist/lib/template_manager/handlers/index.d.ts.map +1 -0
- package/dist/lib/template_manager/handlers/index.js +284 -0
- package/dist/lib/template_manager/handlers/index.js.map +1 -0
- package/dist/lib/template_manager/index.d.ts +56 -0
- package/dist/lib/template_manager/index.d.ts.map +1 -0
- package/dist/lib/template_manager/index.js +68 -0
- package/dist/lib/template_manager/index.js.map +1 -0
- package/dist/lib/template_manager/init.d.ts +54 -0
- package/dist/lib/template_manager/init.d.ts.map +1 -0
- package/dist/lib/template_manager/init.js +85 -0
- package/dist/lib/template_manager/init.js.map +1 -0
- package/dist/lib/template_manager/registry.d.ts +23 -0
- package/dist/lib/template_manager/registry.d.ts.map +1 -0
- package/dist/lib/template_manager/registry.js +33 -0
- package/dist/lib/template_manager/registry.js.map +1 -0
- package/dist/lib/template_manager/seed/sync.d.ts +8 -0
- package/dist/lib/template_manager/seed/sync.d.ts.map +1 -0
- package/dist/lib/template_manager/seed/sync.js +77 -0
- package/dist/lib/template_manager/seed/sync.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/template_manager.d.ts +25 -27
- package/dist/lib/template_manager/template_manager.d.ts.map +1 -0
- package/dist/{template_manager → lib/template_manager}/template_manager.js +41 -55
- package/dist/lib/template_manager/template_manager.js.map +1 -0
- package/dist/{template_manager → lib/template_manager}/types.d.ts +32 -125
- package/dist/lib/template_manager/types.d.ts.map +1 -0
- package/dist/lib/template_manager/types.js +12 -0
- package/dist/lib/template_manager/types.js.map +1 -0
- package/dist/lib/template_manager/utils/index.d.ts.map +1 -0
- package/dist/lib/template_manager/utils/index.js.map +1 -0
- package/dist/lib/template_manager/utils/system_variables.d.ts.map +1 -0
- package/dist/lib/template_manager/utils/system_variables.js.map +1 -0
- package/dist/lib/template_manager/utils/validation.d.ts.map +1 -0
- package/dist/lib/template_manager/utils/validation.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/migrations/002_scope_migration.sql +93 -0
- package/package.json +65 -37
- package/dist/emailer/emailer.d.ts.map +0 -1
- package/dist/emailer/emailer.js.map +0 -1
- package/dist/emailer/index.d.ts.map +0 -1
- package/dist/emailer/index.js.map +0 -1
- package/dist/emailer/providers/index.d.ts.map +0 -1
- package/dist/emailer/providers/index.js.map +0 -1
- package/dist/emailer/providers/pop3_provider.d.ts.map +0 -1
- package/dist/emailer/providers/pop3_provider.js.map +0 -1
- package/dist/emailer/providers/smtp_provider.d.ts.map +0 -1
- package/dist/emailer/providers/smtp_provider.js.map +0 -1
- package/dist/emailer/providers/zeptomail_provider.d.ts.map +0 -1
- package/dist/emailer/providers/zeptomail_provider.js.map +0 -1
- package/dist/emailer/types.d.ts.map +0 -1
- package/dist/emailer/types.js.map +0 -1
- package/dist/emailer/utils/constants.d.ts.map +0 -1
- package/dist/emailer/utils/constants.js.map +0 -1
- package/dist/emailer/utils/index.d.ts.map +0 -1
- package/dist/emailer/utils/index.js.map +0 -1
- package/dist/emailer/utils/logger.d.ts.map +0 -1
- package/dist/emailer/utils/logger.js.map +0 -1
- package/dist/emailer/utils/validation.d.ts.map +0 -1
- package/dist/emailer/utils/validation.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/template_manager/config/config_loader.d.ts.map +0 -1
- package/dist/template_manager/config/config_loader.js +0 -154
- package/dist/template_manager/config/config_loader.js.map +0 -1
- package/dist/template_manager/config/constants.d.ts.map +0 -1
- package/dist/template_manager/config/constants.js.map +0 -1
- package/dist/template_manager/config/index.d.ts.map +0 -1
- package/dist/template_manager/config/index.js.map +0 -1
- package/dist/template_manager/db/category_repository.d.ts.map +0 -1
- package/dist/template_manager/db/category_repository.js.map +0 -1
- package/dist/template_manager/db/index.d.ts +0 -6
- package/dist/template_manager/db/index.d.ts.map +0 -1
- package/dist/template_manager/db/index.js +0 -6
- package/dist/template_manager/db/index.js.map +0 -1
- package/dist/template_manager/db/template_repository.d.ts.map +0 -1
- package/dist/template_manager/db/template_repository.js +0 -674
- package/dist/template_manager/db/template_repository.js.map +0 -1
- package/dist/template_manager/engine/handlebars_engine.d.ts.map +0 -1
- package/dist/template_manager/engine/handlebars_engine.js.map +0 -1
- package/dist/template_manager/engine/index.d.ts.map +0 -1
- package/dist/template_manager/engine/index.js.map +0 -1
- package/dist/template_manager/engine/variable_resolver.d.ts.map +0 -1
- package/dist/template_manager/engine/variable_resolver.js.map +0 -1
- package/dist/template_manager/index.d.ts +0 -32
- package/dist/template_manager/index.d.ts.map +0 -1
- package/dist/template_manager/index.js +0 -41
- package/dist/template_manager/index.js.map +0 -1
- package/dist/template_manager/template_manager.d.ts.map +0 -1
- package/dist/template_manager/template_manager.js.map +0 -1
- package/dist/template_manager/types.d.ts.map +0 -1
- package/dist/template_manager/types.js +0 -11
- package/dist/template_manager/types.js.map +0 -1
- package/dist/template_manager/utils/index.d.ts.map +0 -1
- package/dist/template_manager/utils/index.js.map +0 -1
- package/dist/template_manager/utils/system_variables.d.ts.map +0 -1
- package/dist/template_manager/utils/system_variables.js.map +0 -1
- package/dist/template_manager/utils/validation.d.ts.map +0 -1
- package/dist/template_manager/utils/validation.js.map +0 -1
- /package/dist/{emailer → lib/emailer}/emailer.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/emailer.js +0 -0
- /package/dist/{emailer → lib/emailer}/index.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/index.js +0 -0
- /package/dist/{emailer → lib/emailer}/providers/index.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/providers/index.js +0 -0
- /package/dist/{emailer → lib/emailer}/providers/pop3_provider.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/providers/pop3_provider.js +0 -0
- /package/dist/{emailer → lib/emailer}/providers/smtp_provider.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/providers/smtp_provider.js +0 -0
- /package/dist/{emailer → lib/emailer}/providers/zeptomail_provider.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/types.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/types.js +0 -0
- /package/dist/{emailer → lib/emailer}/utils/constants.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/utils/constants.js +0 -0
- /package/dist/{emailer → lib/emailer}/utils/index.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/utils/index.js +0 -0
- /package/dist/{emailer → lib/emailer}/utils/logger.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/utils/logger.js +0 -0
- /package/dist/{emailer → lib/emailer}/utils/validation.d.ts +0 -0
- /package/dist/{emailer → lib/emailer}/utils/validation.js +0 -0
- /package/dist/{index.d.ts → lib/index.d.ts} +0 -0
- /package/dist/{index.js → lib/index.js} +0 -0
- /package/dist/{template_manager → lib/template_manager}/config/index.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/config/index.js +0 -0
- /package/dist/{template_manager → lib/template_manager}/engine/handlebars_engine.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/engine/handlebars_engine.js +0 -0
- /package/dist/{template_manager → lib/template_manager}/engine/index.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/engine/index.js +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/index.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/index.js +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/system_variables.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/system_variables.js +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/validation.d.ts +0 -0
- /package/dist/{template_manager → lib/template_manager}/utils/validation.js +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Hazo Notify
|
|
2
2
|
|
|
3
|
-
A reusable component library for sending email notifications with template management. Implements Zeptomail API integration with support for text and HTML emails, Handlebars-based email templates, multiple attachments, and comprehensive security features.
|
|
3
|
+
A reusable component library for sending email notifications with scope-aware template management. Implements Zeptomail API integration with support for text and HTML emails, Handlebars-based email templates with multi-tenant resolution, multiple attachments, and comprehensive security features.
|
|
4
|
+
|
|
5
|
+
**Version**: 3.0.0
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
@@ -34,6 +36,18 @@ The package automatically installs required dependencies including `hazo_config`
|
|
|
34
36
|
|
|
35
37
|
**Note:** This package is an ESM module (`"type": "module"`) and requires Node.js 18+ with ESM support.
|
|
36
38
|
|
|
39
|
+
### Upgrading from v1.x to v2.0.0
|
|
40
|
+
|
|
41
|
+
v2.0.0 introduces scope-aware template management, which requires a database migration:
|
|
42
|
+
|
|
43
|
+
1. **Run database migration**: Execute `migrations/002_scope_migration.sql` against your database to rename `org_id` and `root_org_id` columns to `scope_id`.
|
|
44
|
+
2. **Update environment variables**: Add `HAZO_NOTIFY_CORS_ORIGINS` to `.env.local` or configure `cors_allowed_origins` in `hazo_notify_config.ini`.
|
|
45
|
+
3. **Add auth permissions**: Register `notify_templates_admin` and `notify_templates_super_admin` roles in your auth system.
|
|
46
|
+
4. **Initialize template manager**: Call `init_template_manager(...)` in your `instrumentation.ts` or app bootstrap.
|
|
47
|
+
5. **Update Tailwind config** (if using admin UI): Add `@source "../node_modules/hazo_notify/dist";` to your Tailwind CSS entry.
|
|
48
|
+
|
|
49
|
+
The `send_email()` API is fully backward-compatible; no changes required for existing code.
|
|
50
|
+
|
|
37
51
|
### Optional: Enhanced Logging with hazo_logs
|
|
38
52
|
|
|
39
53
|
For structured logging with file rotation, install the optional `hazo_logs` peer dependency:
|
|
@@ -74,30 +88,28 @@ The template manager requires two tables:
|
|
|
74
88
|
|
|
75
89
|
| Table | Purpose |
|
|
76
90
|
|-------|---------|
|
|
77
|
-
| `hazo_notify_template_cat` | Template categories (groups templates per
|
|
91
|
+
| `hazo_notify_template_cat` | Template categories (groups templates per scope) |
|
|
78
92
|
| `hazo_notify_templates` | Email templates with HTML, text, and variable definitions |
|
|
79
93
|
|
|
80
|
-
Both tables
|
|
94
|
+
Both tables use `scope_id` (nullable UUID) for multi-tenant isolation: NULL = global/system templates; non-NULL = tenant-scoped templates. `hazo_notify_templates.template_category_id` references `hazo_notify_template_cat.id`.
|
|
81
95
|
|
|
82
|
-
#### PostgreSQL Schema
|
|
96
|
+
#### PostgreSQL Schema (v2.0.0+)
|
|
83
97
|
|
|
84
98
|
```sql
|
|
85
99
|
CREATE TABLE IF NOT EXISTS hazo_notify_template_cat (
|
|
86
100
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
87
|
-
|
|
88
|
-
root_org_id UUID NOT NULL,
|
|
101
|
+
scope_id UUID,
|
|
89
102
|
template_category_name VARCHAR(100) NOT NULL,
|
|
90
103
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
91
104
|
changed_at TIMESTAMPTZ
|
|
92
105
|
);
|
|
93
106
|
|
|
94
|
-
CREATE INDEX IF NOT EXISTS
|
|
95
|
-
ON hazo_notify_template_cat (
|
|
107
|
+
CREATE INDEX IF NOT EXISTS idx_notify_template_cat_scope
|
|
108
|
+
ON hazo_notify_template_cat (scope_id);
|
|
96
109
|
|
|
97
110
|
CREATE TABLE IF NOT EXISTS hazo_notify_templates (
|
|
98
111
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
99
|
-
|
|
100
|
-
root_org_id UUID NOT NULL,
|
|
112
|
+
scope_id UUID,
|
|
101
113
|
template_category_id UUID NOT NULL REFERENCES hazo_notify_template_cat(id),
|
|
102
114
|
template_variables JSONB DEFAULT '{}',
|
|
103
115
|
template_name VARCHAR(100) NOT NULL,
|
|
@@ -108,35 +120,35 @@ CREATE TABLE IF NOT EXISTS hazo_notify_templates (
|
|
|
108
120
|
changed_at TIMESTAMPTZ
|
|
109
121
|
);
|
|
110
122
|
|
|
111
|
-
CREATE INDEX IF NOT EXISTS
|
|
112
|
-
ON hazo_notify_templates (
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_notify_templates_scope
|
|
124
|
+
ON hazo_notify_templates (scope_id);
|
|
113
125
|
|
|
114
126
|
CREATE INDEX IF NOT EXISTS idx_notify_templates_category
|
|
115
127
|
ON hazo_notify_templates (template_category_id);
|
|
116
128
|
|
|
117
129
|
CREATE INDEX IF NOT EXISTS idx_notify_templates_name
|
|
118
|
-
ON hazo_notify_templates (
|
|
130
|
+
ON hazo_notify_templates (scope_id, template_name);
|
|
119
131
|
```
|
|
120
132
|
|
|
121
|
-
|
|
133
|
+
**Note**: `scope_id` is nullable. NULL represents global/system templates; non-NULL represents tenant-scoped templates.
|
|
134
|
+
|
|
135
|
+
#### SQLite Schema (v2.0.0+)
|
|
122
136
|
|
|
123
137
|
```sql
|
|
124
138
|
CREATE TABLE IF NOT EXISTS hazo_notify_template_cat (
|
|
125
139
|
id TEXT PRIMARY KEY,
|
|
126
|
-
|
|
127
|
-
root_org_id TEXT NOT NULL,
|
|
140
|
+
scope_id TEXT,
|
|
128
141
|
template_category_name TEXT NOT NULL,
|
|
129
142
|
created_at TEXT DEFAULT (datetime('now')),
|
|
130
143
|
changed_at TEXT
|
|
131
144
|
);
|
|
132
145
|
|
|
133
|
-
CREATE INDEX IF NOT EXISTS
|
|
134
|
-
ON hazo_notify_template_cat (
|
|
146
|
+
CREATE INDEX IF NOT EXISTS idx_notify_template_cat_scope
|
|
147
|
+
ON hazo_notify_template_cat (scope_id);
|
|
135
148
|
|
|
136
149
|
CREATE TABLE IF NOT EXISTS hazo_notify_templates (
|
|
137
150
|
id TEXT PRIMARY KEY,
|
|
138
|
-
|
|
139
|
-
root_org_id TEXT NOT NULL,
|
|
151
|
+
scope_id TEXT,
|
|
140
152
|
template_category_id TEXT NOT NULL REFERENCES hazo_notify_template_cat(id),
|
|
141
153
|
template_variables TEXT DEFAULT '{}',
|
|
142
154
|
template_name TEXT NOT NULL,
|
|
@@ -147,21 +159,24 @@ CREATE TABLE IF NOT EXISTS hazo_notify_templates (
|
|
|
147
159
|
changed_at TEXT
|
|
148
160
|
);
|
|
149
161
|
|
|
150
|
-
CREATE INDEX IF NOT EXISTS
|
|
151
|
-
ON hazo_notify_templates (
|
|
162
|
+
CREATE INDEX IF NOT EXISTS idx_notify_templates_scope
|
|
163
|
+
ON hazo_notify_templates (scope_id);
|
|
152
164
|
|
|
153
165
|
CREATE INDEX IF NOT EXISTS idx_notify_templates_category
|
|
154
166
|
ON hazo_notify_templates (template_category_id);
|
|
155
167
|
|
|
156
168
|
CREATE INDEX IF NOT EXISTS idx_notify_templates_name
|
|
157
|
-
ON hazo_notify_templates (
|
|
169
|
+
ON hazo_notify_templates (scope_id, template_name);
|
|
158
170
|
```
|
|
159
171
|
|
|
172
|
+
**Note**: `scope_id` is nullable. NULL represents global/system templates; non-NULL represents tenant-scoped templates.
|
|
173
|
+
|
|
160
174
|
#### Type Mapping (PostgreSQL ↔ SQLite)
|
|
161
175
|
|
|
162
176
|
| Concept | PostgreSQL | SQLite |
|
|
163
177
|
|---------|------------|--------|
|
|
164
178
|
| Primary / foreign keys | `UUID` (with `gen_random_uuid()` default) | `TEXT` (UUID string supplied by app) |
|
|
179
|
+
| Scope key (`scope_id`) | `UUID` (nullable) | `TEXT` (nullable, UUID string or NULL) |
|
|
165
180
|
| JSON column (`template_variables`) | `JSONB` | `TEXT` (serialized JSON) |
|
|
166
181
|
| Timestamps | `TIMESTAMPTZ` with `NOW()` default | `TEXT` with `datetime('now')` default |
|
|
167
182
|
|
|
@@ -298,16 +313,19 @@ const result = await send_email({
|
|
|
298
313
|
```typescript
|
|
299
314
|
{
|
|
300
315
|
success: true,
|
|
301
|
-
message_id
|
|
316
|
+
// message_id is the request_id Zeptomail assigns to the send. Use it for
|
|
317
|
+
// delivery lookup in Zeptomail logs.
|
|
318
|
+
message_id: '7a6803.5aa56c8edd39e440.m1.…',
|
|
302
319
|
message: 'Email sent successfully',
|
|
303
320
|
raw_response: {
|
|
304
|
-
status:
|
|
305
|
-
status_text: '
|
|
321
|
+
status: 201,
|
|
322
|
+
status_text: '',
|
|
306
323
|
headers: { /* response headers */ },
|
|
307
324
|
body: {
|
|
308
|
-
data: {
|
|
309
|
-
|
|
310
|
-
|
|
325
|
+
data: [{ code: 'EM_104', additional_info: [], message: 'Email request received' }],
|
|
326
|
+
message: 'OK',
|
|
327
|
+
request_id: '7a6803.5aa56c8edd39e440.m1.…',
|
|
328
|
+
object: 'email'
|
|
311
329
|
}
|
|
312
330
|
}
|
|
313
331
|
}
|
|
@@ -552,20 +570,18 @@ const result = await send_email({
|
|
|
552
570
|
```typescript
|
|
553
571
|
{
|
|
554
572
|
success: true,
|
|
555
|
-
message_id
|
|
573
|
+
// message_id is Zeptomail's request_id (the unique identifier for the send)
|
|
574
|
+
message_id: '7a6803.5aa56c8edd39e440.m1.…',
|
|
556
575
|
message: 'Email sent successfully',
|
|
557
576
|
raw_response: {
|
|
558
|
-
status:
|
|
559
|
-
status_text: '
|
|
560
|
-
headers: {
|
|
561
|
-
'content-type': 'application/json',
|
|
562
|
-
'content-length': '156',
|
|
563
|
-
// ... other headers
|
|
564
|
-
},
|
|
577
|
+
status: 201,
|
|
578
|
+
status_text: '',
|
|
579
|
+
headers: { /* response headers */ },
|
|
565
580
|
body: {
|
|
566
|
-
data: {
|
|
567
|
-
|
|
568
|
-
|
|
581
|
+
data: [{ code: 'EM_104', additional_info: [], message: 'Email request received' }],
|
|
582
|
+
message: 'OK',
|
|
583
|
+
request_id: '7a6803.5aa56c8edd39e440.m1.…',
|
|
584
|
+
object: 'email'
|
|
569
585
|
}
|
|
570
586
|
}
|
|
571
587
|
}
|
|
@@ -797,9 +813,115 @@ console.log('Emailer module:', config.emailer_module);
|
|
|
797
813
|
console.log('From email:', config.from_email);
|
|
798
814
|
```
|
|
799
815
|
|
|
816
|
+
## Template Manager (v2.0.0+)
|
|
817
|
+
|
|
818
|
+
The scope-aware template manager allows you to define, store, and render email templates with multi-tenant isolation. Templates are stored in the database and support hierarchical resolution: tenant-scoped templates take precedence over global system templates.
|
|
819
|
+
|
|
820
|
+
### Initialization
|
|
821
|
+
|
|
822
|
+
In your app's `instrumentation.ts` or bootstrap code, initialize the template manager once:
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
import { init_template_manager, register_template_type } from 'hazo_notify/template_manager';
|
|
826
|
+
import { createHazoConnect } from 'hazo_connect/server';
|
|
827
|
+
|
|
828
|
+
export async function register() {
|
|
829
|
+
const hazo_connect = await createHazoConnect();
|
|
830
|
+
|
|
831
|
+
// Register template types (optional, but recommended)
|
|
832
|
+
register_template_type({
|
|
833
|
+
type_id: 'welcome_email',
|
|
834
|
+
display_name: 'Welcome Email',
|
|
835
|
+
variables: {
|
|
836
|
+
user_name: { type: 'string', description: 'User full name' },
|
|
837
|
+
login_url: { type: 'string', description: 'Link to login page' }
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
// Initialize template manager with optional scope resolver
|
|
842
|
+
await init_template_manager({
|
|
843
|
+
hazo_connect,
|
|
844
|
+
scope_resolver: async (scope_id) => {
|
|
845
|
+
// Optional: resolve tenant hierarchy (e.g., org > parent_org > system)
|
|
846
|
+
// Return { scope_id, parent_scope_id? }
|
|
847
|
+
return { scope_id };
|
|
848
|
+
},
|
|
849
|
+
system_templates: [
|
|
850
|
+
// Optional: define system templates to auto-seed
|
|
851
|
+
{
|
|
852
|
+
template_name: 'welcome_email',
|
|
853
|
+
type_id: 'welcome_email',
|
|
854
|
+
template_html: '<h1>Welcome, {{user_name}}!</h1><p><a href="{{login_url}}">Log in here</a></p>',
|
|
855
|
+
template_text: 'Welcome, {{user_name}}! Visit {{login_url}} to log in.'
|
|
856
|
+
}
|
|
857
|
+
]
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
### Sending Template Emails
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
import { send_template_email } from 'hazo_notify/template_manager';
|
|
866
|
+
|
|
867
|
+
const result = await send_template_email({
|
|
868
|
+
template_name: 'welcome_email',
|
|
869
|
+
variables: {
|
|
870
|
+
user_name: 'Alice',
|
|
871
|
+
login_url: 'https://app.example.com/login'
|
|
872
|
+
},
|
|
873
|
+
to: 'alice@example.com',
|
|
874
|
+
subject: 'Welcome to Our App'
|
|
875
|
+
}, hazo_connect, scope_id);
|
|
876
|
+
|
|
877
|
+
if (result.success) {
|
|
878
|
+
console.log('Email sent:', result.message_id);
|
|
879
|
+
} else {
|
|
880
|
+
console.error('Failed:', result.error);
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### Admin UI
|
|
885
|
+
|
|
886
|
+
Mount the admin UI in a protected Next.js page:
|
|
887
|
+
|
|
888
|
+
```typescript
|
|
889
|
+
// app/admin/templates/page.tsx
|
|
890
|
+
import { TemplateManagerAdmin, TemplateGlobalsAdmin } from 'hazo_notify/template_manager_admin';
|
|
891
|
+
import { requireAdmin } from '@/lib/auth'; // Your auth check
|
|
892
|
+
|
|
893
|
+
export default async function TemplatesPage() {
|
|
894
|
+
await requireAdmin();
|
|
895
|
+
|
|
896
|
+
return (
|
|
897
|
+
<div className="space-y-8">
|
|
898
|
+
<section>
|
|
899
|
+
<h1>Tenant Templates</h1>
|
|
900
|
+
<TemplateManagerAdmin scope_id={current_tenant_id} />
|
|
901
|
+
</section>
|
|
902
|
+
<section>
|
|
903
|
+
<h1>System Templates (Global)</h1>
|
|
904
|
+
<TemplateGlobalsAdmin />
|
|
905
|
+
</section>
|
|
906
|
+
</div>
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Permission requirements**:
|
|
912
|
+
- `notify_templates_admin` — read/write templates
|
|
913
|
+
- `notify_templates_super_admin` — delete system templates
|
|
914
|
+
|
|
915
|
+
**Tailwind v4 requirement**: If using the admin UI, add this to your `globals.css`:
|
|
916
|
+
|
|
917
|
+
```css
|
|
918
|
+
@import "tailwindcss";
|
|
919
|
+
@source "../node_modules/hazo_notify/dist";
|
|
920
|
+
```
|
|
921
|
+
|
|
800
922
|
### Template Manager API
|
|
801
923
|
|
|
802
|
-
#### `send_template_email(options, hazo_connect,
|
|
924
|
+
#### `send_template_email(options, hazo_connect, scope_id, config?): Promise<SendTemplateEmailResponse>`
|
|
803
925
|
|
|
804
926
|
Render a named template and send it as an email.
|
|
805
927
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TemplateCategory } from '../../lib/template_manager/index.js';
|
|
2
|
+
interface CategoryDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
on_close: () => void;
|
|
5
|
+
on_save: (name: string) => void;
|
|
6
|
+
category?: TemplateCategory | null;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function CategoryDialog({ open, on_close, on_save, category, loading, }: CategoryDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default CategoryDialog;
|
|
11
|
+
//# sourceMappingURL=category_dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"category_dialog.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/category_dialog.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAE5E,UAAU,mBAAmB;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,OAAe,GAChB,EAAE,mBAAmB,2CAoFrB;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Category Dialog Component
|
|
5
|
+
*
|
|
6
|
+
* Dialog for creating and editing template categories
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import { Button, Input, Label, HazoUiDialogRoot, HazoUiDialogContent, HazoUiDialogDescription, HazoUiDialogFooter, HazoUiDialogHeader, HazoUiDialogTitle, } from 'hazo_ui';
|
|
10
|
+
export function CategoryDialog({ open, on_close, on_save, category, loading = false, }) {
|
|
11
|
+
const [name, set_name] = useState('');
|
|
12
|
+
const [error, set_error] = useState(null);
|
|
13
|
+
const is_edit = !!category;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (open) {
|
|
16
|
+
set_name(category?.template_category_name || '');
|
|
17
|
+
set_error(null);
|
|
18
|
+
}
|
|
19
|
+
}, [open, category]);
|
|
20
|
+
const handle_submit = (e) => {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
const trimmed = name.trim();
|
|
23
|
+
if (!trimmed) {
|
|
24
|
+
set_error('Category name is required');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (trimmed.length > 100) {
|
|
28
|
+
set_error('Category name must be 100 characters or less');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
on_save(trimmed);
|
|
32
|
+
};
|
|
33
|
+
return (_jsx(HazoUiDialogRoot, { open: open, onOpenChange: (open) => !open && on_close(), children: _jsx(HazoUiDialogContent, { className: "cls_category_dialog sm:max-w-[425px]", children: _jsxs("form", { onSubmit: handle_submit, children: [_jsxs(HazoUiDialogHeader, { children: [_jsx(HazoUiDialogTitle, { children: is_edit ? 'Edit Category' : 'New Category' }), _jsx(HazoUiDialogDescription, { children: is_edit
|
|
34
|
+
? 'Update the category name.'
|
|
35
|
+
: 'Create a new template category to organize your email templates.' })] }), _jsx("div", { className: "cls_dialog_body grid gap-4 py-4", children: _jsxs("div", { className: "cls_form_field grid gap-2", children: [_jsx(Label, { htmlFor: "category_name", children: "Category Name" }), _jsx(Input, { id: "category_name", value: name, onChange: (e) => {
|
|
36
|
+
set_name(e.target.value);
|
|
37
|
+
set_error(null);
|
|
38
|
+
}, placeholder: "e.g., Marketing, Transactional, Notifications", disabled: loading, autoFocus: true }), error && (_jsx("p", { className: "cls_error_message text-sm text-destructive", children: error }))] }) }), _jsxs(HazoUiDialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: on_close, disabled: loading, children: "Cancel" }), _jsx(Button, { type: "submit", disabled: loading, children: loading ? 'Saving...' : is_edit ? 'Save Changes' : 'Create' })] })] }) }) }));
|
|
39
|
+
}
|
|
40
|
+
export default CategoryDialog;
|
|
41
|
+
//# sourceMappingURL=category_dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"category_dialog.js","sourceRoot":"","sources":["../../../src/components/template_manager/category_dialog.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TemplateCategory, EmailTemplate, CategoryTreeItem } from '../../lib/template_manager/index.js';
|
|
2
|
+
interface CategoryTreeProps {
|
|
3
|
+
categories: CategoryTreeItem[];
|
|
4
|
+
selected_template_id?: string | null;
|
|
5
|
+
on_select_template: (template: EmailTemplate) => void;
|
|
6
|
+
on_create_category: () => void;
|
|
7
|
+
on_delete_category: (category: TemplateCategory) => void;
|
|
8
|
+
on_create_template: (category: TemplateCategory) => void;
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function CategoryTree({ categories, selected_template_id, on_select_template, on_create_category, on_delete_category, on_create_template, loading, }: CategoryTreeProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export default CategoryTree;
|
|
13
|
+
//# sourceMappingURL=category_tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"category_tree.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/category_tree.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EACjB,MAAM,qCAAqC,CAAC;AAE7C,UAAU,iBAAiB;IACzB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,OAAe,GAChB,EAAE,iBAAiB,2CAiFnB;AAwHD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Category Tree Component
|
|
5
|
+
*
|
|
6
|
+
* Displays template categories in an expandable tree structure
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import { ChevronRight, ChevronDown, Plus, Trash2, FileText, FolderOpen } from 'lucide-react';
|
|
10
|
+
import { Button, Card, CardContent, CardHeader, CardTitle, ScrollArea, Collapsible, CollapsibleContent, CollapsibleTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from 'hazo_ui';
|
|
11
|
+
import { cn } from '../../lib/utils.js';
|
|
12
|
+
export function CategoryTree({ categories, selected_template_id, on_select_template, on_create_category, on_delete_category, on_create_template, loading = false, }) {
|
|
13
|
+
const [expanded_categories, set_expanded_categories] = useState(new Set());
|
|
14
|
+
const toggle_category = (category_id) => {
|
|
15
|
+
set_expanded_categories((prev) => {
|
|
16
|
+
const next = new Set(prev);
|
|
17
|
+
if (next.has(category_id)) {
|
|
18
|
+
next.delete(category_id);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
next.add(category_id);
|
|
22
|
+
}
|
|
23
|
+
return next;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
// Expand categories that contain the selected template
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (selected_template_id) {
|
|
29
|
+
for (const item of categories) {
|
|
30
|
+
const has_selected = item.templates.some((t) => t.id === selected_template_id);
|
|
31
|
+
if (has_selected) {
|
|
32
|
+
set_expanded_categories((prev) => new Set(prev).add(item.category.id));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}, [selected_template_id, categories]);
|
|
37
|
+
return (_jsxs(Card, { className: "cls_category_tree h-full", children: [_jsx(CardHeader, { className: "cls_category_tree_header pb-2", children: _jsx("div", { className: "cls_category_tree_title_row flex items-center justify-between", children: _jsx(CardTitle, { className: "text-lg", children: "Templates" }) }) }), _jsx(CardContent, { className: "cls_category_tree_content p-2", children: _jsx(ScrollArea, { className: "h-[calc(100vh-200px)]", children: _jsxs("div", { className: "cls_category_list space-y-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", className: "cls_add_category_btn w-full justify-start text-primary hover:text-primary hover:bg-primary/10", onClick: on_create_category, disabled: loading, children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "New Category"] }), loading ? (_jsx("div", { className: "cls_loading_state p-4 text-center text-muted-foreground", children: "Loading..." })) : categories.length === 0 ? (_jsx("div", { className: "cls_empty_state p-4 text-center text-muted-foreground", children: "No categories yet" })) : (categories.map((item) => (_jsx(CategoryItem, { item: item, is_expanded: expanded_categories.has(item.category.id), selected_template_id: selected_template_id, on_toggle: () => toggle_category(item.category.id), on_select_template: on_select_template, on_delete_category: () => on_delete_category(item.category), on_create_template: () => on_create_template(item.category) }, item.category.id))))] }) }) })] }));
|
|
38
|
+
}
|
|
39
|
+
function CategoryItem({ item, is_expanded, selected_template_id, on_toggle, on_select_template, on_delete_category, on_create_template, }) {
|
|
40
|
+
const { category, templates } = item;
|
|
41
|
+
return (_jsx(Collapsible, { open: is_expanded, onOpenChange: on_toggle, children: _jsxs("div", { className: "cls_category_item group", children: [_jsxs("div", { className: "cls_category_header flex items-center", children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "cls_category_toggle flex-1 justify-start p-2 h-auto", children: [is_expanded ? (_jsx(ChevronDown, { className: "h-4 w-4 mr-1 shrink-0" })) : (_jsx(ChevronRight, { className: "h-4 w-4 mr-1 shrink-0" })), _jsx(FolderOpen, { className: "h-4 w-4 mr-2 text-muted-foreground shrink-0" }), _jsx("span", { className: "truncate", children: category.template_category_name }), _jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: templates.length })] }) }), _jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "cls_delete_category_btn h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity text-destructive hover:text-destructive hover:bg-destructive/10", children: _jsx(Trash2, { className: "h-4 w-4" }) }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete Category" }), _jsxs(AlertDialogDescription, { children: ["Are you sure you want to delete \"", category.template_category_name, "\"? This will also delete all ", templates.length, " templates in this category. This action cannot be undone."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsx(AlertDialogAction, { onClick: on_delete_category, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Delete" })] })] })] })] }), _jsx(CollapsibleContent, { children: _jsxs("div", { className: "cls_template_list ml-4 border-l border-border pl-2 space-y-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", className: "cls_add_template_btn w-full justify-start text-sm text-primary hover:text-primary hover:bg-primary/10 h-8", onClick: on_create_template, children: [_jsx(Plus, { className: "h-3 w-3 mr-2" }), "New Template"] }), templates.map((template) => (_jsxs(Button, { variant: "ghost", size: "sm", className: cn('cls_template_item w-full justify-start text-sm h-8', selected_template_id === template.id &&
|
|
42
|
+
'bg-accent text-accent-foreground'), onClick: () => on_select_template(template), children: [_jsx(FileText, { className: "h-3 w-3 mr-2 shrink-0" }), _jsx("span", { className: "truncate", children: template.template_name }), template.scope_id === null && (_jsx("span", { className: "ml-auto text-xs text-muted-foreground bg-muted px-1 rounded", children: "global" }))] }, template.id)))] }) })] }) }));
|
|
43
|
+
}
|
|
44
|
+
export default CategoryTree;
|
|
45
|
+
//# sourceMappingURL=category_tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"category_tree.js","sourceRoot":"","sources":["../../../src/components/template_manager/category_tree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CategoryTree } from './category_tree.js';
|
|
2
|
+
export { CategoryDialog } from './category_dialog.js';
|
|
3
|
+
export { TemplateDialog } from './template_dialog.js';
|
|
4
|
+
export { TemplateEditor } from './template_editor.js';
|
|
5
|
+
export { PreviewDialog } from './preview_dialog.js';
|
|
6
|
+
export { TemplateManagerAdmin } from './template_manager_admin.js';
|
|
7
|
+
export type { TemplateManagerAdminProps } from './template_manager_admin.js';
|
|
8
|
+
export { TemplateGlobalsAdmin } from './template_globals_admin.js';
|
|
9
|
+
export type { TemplateGlobalsAdminProps } from './template_globals_admin.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CategoryTree } from './category_tree.js';
|
|
2
|
+
export { CategoryDialog } from './category_dialog.js';
|
|
3
|
+
export { TemplateDialog } from './template_dialog.js';
|
|
4
|
+
export { TemplateEditor } from './template_editor.js';
|
|
5
|
+
export { PreviewDialog } from './preview_dialog.js';
|
|
6
|
+
export { TemplateManagerAdmin } from './template_manager_admin.js';
|
|
7
|
+
export { TemplateGlobalsAdmin } from './template_globals_admin.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/template_manager/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface PreviewDialogProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
on_close: () => void;
|
|
4
|
+
rendered_html?: string;
|
|
5
|
+
rendered_text?: string;
|
|
6
|
+
loading?: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function PreviewDialog({ open, on_close, rendered_html, rendered_text, loading, error, }: PreviewDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default PreviewDialog;
|
|
11
|
+
//# sourceMappingURL=preview_dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview_dialog.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/preview_dialog.tsx"],"names":[],"mappings":"AAmBA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,aAAa,EACb,OAAe,EACf,KAAK,GACN,EAAE,kBAAkB,2CAwEpB;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Preview Dialog Component
|
|
5
|
+
*
|
|
6
|
+
* Dialog for previewing rendered email templates
|
|
7
|
+
*/
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
import { Button, ScrollArea, HazoUiDialogRoot, HazoUiDialogContent, HazoUiDialogDescription, HazoUiDialogHeader, HazoUiDialogTitle, } from 'hazo_ui';
|
|
10
|
+
export function PreviewDialog({ open, on_close, rendered_html, rendered_text, loading = false, error, }) {
|
|
11
|
+
const [active_tab, set_active_tab] = useState('html');
|
|
12
|
+
return (_jsx(HazoUiDialogRoot, { open: open, onOpenChange: (open) => !open && on_close(), children: _jsxs(HazoUiDialogContent, { className: "cls_preview_dialog max-w-4xl max-h-[90vh]", children: [_jsxs(HazoUiDialogHeader, { children: [_jsx(HazoUiDialogTitle, { children: "Template Preview" }), _jsx(HazoUiDialogDescription, { children: "Preview how your email will look when rendered with variables." })] }), _jsxs("div", { className: "cls_preview_tabs flex gap-2 mb-4", children: [_jsx(Button, { variant: active_tab === 'html' ? 'default' : 'outline', size: "sm", onClick: () => set_active_tab('html'), children: "HTML Preview" }), _jsx(Button, { variant: active_tab === 'text' ? 'default' : 'outline', size: "sm", onClick: () => set_active_tab('text'), children: "Plain Text" })] }), _jsx(ScrollArea, { className: "h-[60vh]", children: loading ? (_jsx("div", { className: "cls_preview_loading flex items-center justify-center h-full", children: _jsx("p", { className: "text-muted-foreground", children: "Rendering preview..." }) })) : error ? (_jsxs("div", { className: "cls_preview_error p-4 bg-destructive/10 rounded-lg", children: [_jsx("p", { className: "text-destructive font-medium", children: "Error rendering preview" }), _jsx("p", { className: "text-sm text-destructive/80 mt-1", children: error })] })) : active_tab === 'html' ? (_jsx("div", { className: "cls_html_preview", children: rendered_html ? (_jsx("iframe", { srcDoc: rendered_html, title: "Email Preview", className: "w-full h-[55vh] border rounded-lg bg-white", sandbox: "allow-same-origin" })) : (_jsx("div", { className: "cls_empty_preview p-8 text-center text-muted-foreground border rounded-lg", children: "No HTML content to preview" })) })) : (_jsx("div", { className: "cls_text_preview", children: rendered_text ? (_jsx("pre", { className: "p-4 bg-muted rounded-lg text-sm whitespace-pre-wrap font-mono", children: rendered_text })) : (_jsx("div", { className: "cls_empty_preview p-8 text-center text-muted-foreground border rounded-lg", children: "No plain text content to preview" })) })) })] }) }));
|
|
13
|
+
}
|
|
14
|
+
export default PreviewDialog;
|
|
15
|
+
//# sourceMappingURL=preview_dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview_dialog.js","sourceRoot":"","sources":["../../../src/components/template_manager/preview_dialog.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EACL,MAAM,EACN,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAWjB,MAAM,UAAU,aAAa,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,aAAa,EACb,OAAO,GAAG,KAAK,EACf,KAAK,GACc;IACnB,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAkB,MAAM,CAAC,CAAC;IAEvE,OAAO,CACL,KAAC,gBAAgB,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,QAAQ,EAAE,YACvE,MAAC,mBAAmB,IAAC,SAAS,EAAC,2CAA2C,aACxE,MAAC,kBAAkB,eACjB,KAAC,iBAAiB,mCAAqC,EACvD,KAAC,uBAAuB,iFAEE,IACP,EAErB,eAAK,SAAS,EAAC,kCAAkC,aAC/C,KAAC,MAAM,IACL,OAAO,EAAE,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EACtD,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,6BAG9B,EACT,KAAC,MAAM,IACL,OAAO,EAAE,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EACtD,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,2BAG9B,IACL,EAEN,KAAC,UAAU,IAAC,SAAS,EAAC,UAAU,YAC7B,OAAO,CAAC,CAAC,CAAC,CACT,cAAK,SAAS,EAAC,6DAA6D,YAC1E,YAAG,SAAS,EAAC,uBAAuB,qCAAyB,GACzD,CACP,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CACV,eAAK,SAAS,EAAC,oDAAoD,aACjE,YAAG,SAAS,EAAC,8BAA8B,wCAA4B,EACvE,YAAG,SAAS,EAAC,kCAAkC,YAAE,KAAK,GAAK,IACvD,CACP,CAAC,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,CAC1B,cAAK,SAAS,EAAC,kBAAkB,YAC9B,aAAa,CAAC,CAAC,CAAC,CACf,iBACE,MAAM,EAAE,aAAa,EACrB,KAAK,EAAC,eAAe,EACrB,SAAS,EAAC,4CAA4C,EACtD,OAAO,EAAC,mBAAmB,GAC3B,CACH,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,2EAA2E,2CAEpF,CACP,GACG,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,kBAAkB,YAC9B,aAAa,CAAC,CAAC,CAAC,CACf,cAAK,SAAS,EAAC,+DAA+D,YAC3E,aAAa,GACV,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,2EAA2E,iDAEpF,CACP,GACG,CACP,GACU,IACO,GACL,CACpB,CAAC;AACJ,CAAC;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TemplateCategory, TemplateTypeDefinition } from '../../lib/template_manager/index.js';
|
|
2
|
+
interface TemplateDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
on_close: () => void;
|
|
5
|
+
on_save: (template_name: string) => void;
|
|
6
|
+
category: TemplateCategory | null;
|
|
7
|
+
template_types: TemplateTypeDefinition[];
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function TemplateDialog({ open, on_close, on_save, category, template_types, loading, }: TemplateDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export default TemplateDialog;
|
|
12
|
+
//# sourceMappingURL=template_dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template_dialog.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/template_dialog.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAEpG,UAAU,mBAAmB;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,cAAc,EAAE,sBAAsB,EAAE,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,cAAc,EACd,OAAe,GAChB,EAAE,mBAAmB,2CAiHrB;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Template Dialog Component
|
|
5
|
+
*
|
|
6
|
+
* Dialog for creating new email templates
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import { Button, Input, Label, HazoUiDialogRoot, HazoUiDialogContent, HazoUiDialogDescription, HazoUiDialogFooter, HazoUiDialogHeader, HazoUiDialogTitle, } from 'hazo_ui';
|
|
10
|
+
export function TemplateDialog({ open, on_close, on_save, category, template_types, loading = false, }) {
|
|
11
|
+
const [name, set_name] = useState('');
|
|
12
|
+
const [error, set_error] = useState(null);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (open) {
|
|
15
|
+
set_name('');
|
|
16
|
+
set_error(null);
|
|
17
|
+
}
|
|
18
|
+
}, [open]);
|
|
19
|
+
const handle_submit = (e) => {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
const trimmed = name.trim().toLowerCase().replace(/\s+/g, '_');
|
|
22
|
+
if (!trimmed) {
|
|
23
|
+
set_error('Template name is required');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Validate format
|
|
27
|
+
if (!/^[a-z][a-z0-9_]*$/.test(trimmed)) {
|
|
28
|
+
set_error('Template name must start with a letter and contain only lowercase letters, numbers, and underscores');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (trimmed.length > 100) {
|
|
32
|
+
set_error('Template name must be 100 characters or less');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
on_save(trimmed);
|
|
36
|
+
};
|
|
37
|
+
return (_jsx(HazoUiDialogRoot, { open: open, onOpenChange: (open) => !open && on_close(), children: _jsx(HazoUiDialogContent, { className: "cls_template_dialog sm:max-w-[500px]", children: _jsxs("form", { onSubmit: handle_submit, children: [_jsxs(HazoUiDialogHeader, { children: [_jsx(HazoUiDialogTitle, { children: "New Email Template" }), _jsxs(HazoUiDialogDescription, { children: ["Create a new email template in", ' ', _jsx("strong", { children: category?.template_category_name || 'this category' }), "."] })] }), _jsxs("div", { className: "cls_dialog_body grid gap-4 py-4", children: [_jsxs("div", { className: "cls_form_field grid gap-2", children: [_jsx(Label, { htmlFor: "template_name", children: "Template Name" }), _jsx(Input, { id: "template_name", value: name, onChange: (e) => {
|
|
38
|
+
set_name(e.target.value);
|
|
39
|
+
set_error(null);
|
|
40
|
+
}, placeholder: "e.g., welcome_email, order_confirmation", disabled: loading, autoFocus: true }), _jsx("p", { className: "cls_hint text-xs text-muted-foreground", children: "Use lowercase letters, numbers, and underscores only." }), error && (_jsx("p", { className: "cls_error_message text-sm text-destructive", children: error }))] }), template_types.length > 0 && (_jsxs("div", { className: "cls_template_types", children: [_jsx(Label, { className: "text-sm text-muted-foreground", children: "Suggested template types:" }), _jsx("div", { className: "cls_type_buttons flex flex-wrap gap-2 mt-2", children: template_types.map((type) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
|
|
41
|
+
set_name(type.template_name);
|
|
42
|
+
set_error(null);
|
|
43
|
+
}, className: "text-xs", children: type.template_label }, type.template_name))) })] }))] }), _jsxs(HazoUiDialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: on_close, disabled: loading, children: "Cancel" }), _jsx(Button, { type: "submit", disabled: loading, children: loading ? 'Creating...' : 'Create Template' })] })] }) }) }));
|
|
44
|
+
}
|
|
45
|
+
export default TemplateDialog;
|
|
46
|
+
//# sourceMappingURL=template_dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template_dialog.js","sourceRoot":"","sources":["../../../src/components/template_manager/template_dialog.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EmailTemplate, TemplateTypeDefinition } from '../../lib/template_manager/index.js';
|
|
2
|
+
interface TemplateEditorProps {
|
|
3
|
+
template: EmailTemplate | null;
|
|
4
|
+
template_types: TemplateTypeDefinition[];
|
|
5
|
+
on_save: (html: string, text: string) => void;
|
|
6
|
+
on_delete: () => void;
|
|
7
|
+
on_preview: (html: string, text: string) => void;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function TemplateEditor({ template, template_types, on_save, on_delete, on_preview, loading, }: TemplateEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export default TemplateEditor;
|
|
12
|
+
//# sourceMappingURL=template_editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template_editor.d.ts","sourceRoot":"","sources":["../../../src/components/template_manager/template_editor.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EACvB,MAAM,qCAAqC,CAAC;AAG7C,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,sBAAsB,EAAE,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,EACT,UAAU,EACV,OAAe,GAChB,EAAE,mBAAmB,2CAoJrB;AAED,eAAe,cAAc,CAAC"}
|