domma-cms 0.6.14 → 0.6.16

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 (46) hide show
  1. package/admin/js/app.js +4 -4
  2. package/admin/js/config/sidebar-config.js +1 -1
  3. package/admin/js/lib/markdown-toolbar.js +8 -6
  4. package/admin/js/templates/pages.html +5 -1
  5. package/admin/js/views/pages.js +2 -2
  6. package/config/plugins.json +11 -1
  7. package/package.json +1 -1
  8. package/plugins/analytics/stats.json +3 -3
  9. package/plugins/form-builder/data/forms/contacts.json +66 -0
  10. package/plugins/form-builder/data/forms/enquiries.json +103 -0
  11. package/plugins/form-builder/data/forms/feedback.json +131 -0
  12. package/plugins/form-builder/data/forms/notes.json +79 -0
  13. package/plugins/form-builder/data/forms/to-do.json +100 -0
  14. package/plugins/form-builder/data/submissions/contacts.json +1 -0
  15. package/plugins/form-builder/data/submissions/enquiries.json +1 -0
  16. package/plugins/form-builder/data/submissions/feedback.json +1 -0
  17. package/plugins/form-builder/data/submissions/notes.json +1 -0
  18. package/plugins/form-builder/data/submissions/to-do.json +1 -0
  19. package/plugins/job-board/admin/templates/application-detail.html +40 -0
  20. package/plugins/job-board/admin/templates/applications.html +10 -0
  21. package/plugins/job-board/admin/templates/companies.html +24 -0
  22. package/plugins/job-board/admin/templates/dashboard.html +36 -0
  23. package/plugins/job-board/admin/templates/job-editor.html +17 -0
  24. package/plugins/job-board/admin/templates/jobs.html +15 -0
  25. package/plugins/job-board/admin/templates/profile.html +17 -0
  26. package/plugins/job-board/admin/views/application-detail.js +62 -0
  27. package/plugins/job-board/admin/views/applications.js +47 -0
  28. package/plugins/job-board/admin/views/companies.js +104 -0
  29. package/plugins/job-board/admin/views/dashboard.js +88 -0
  30. package/plugins/job-board/admin/views/job-editor.js +86 -0
  31. package/plugins/job-board/admin/views/jobs.js +53 -0
  32. package/plugins/job-board/admin/views/profile.js +47 -0
  33. package/plugins/job-board/config.js +6 -0
  34. package/plugins/job-board/plugin.js +466 -0
  35. package/plugins/job-board/plugin.json +40 -0
  36. package/plugins/job-board/schemas/jb-agent-companies.json +17 -0
  37. package/plugins/job-board/schemas/jb-applications.json +20 -0
  38. package/plugins/job-board/schemas/jb-candidate-profiles.json +20 -0
  39. package/plugins/job-board/schemas/jb-companies.json +21 -0
  40. package/plugins/job-board/schemas/jb-jobs.json +23 -0
  41. package/public/css/site.css +1 -1
  42. package/server/routes/api/collections.js +4 -0
  43. package/server/routes/api/plugins.js +9 -1
  44. package/server/server.js +1 -0
  45. package/server/services/plugins.js +30 -0
  46. package/server/services/userTypes.js +227 -0
@@ -0,0 +1,131 @@
1
+ {
2
+ "slug": "feedback",
3
+ "title": "Feedback",
4
+ "description": "Share your feedback with us",
5
+ "fields": [
6
+ {
7
+ "name": "name",
8
+ "type": "string",
9
+ "label": "Your Name",
10
+ "required": true,
11
+ "placeholder": "Please enter your full name",
12
+ "helper": "Please enter your full name",
13
+ "validation": {
14
+ "min": 2,
15
+ "max": 100
16
+ }
17
+ },
18
+ {
19
+ "name": "email",
20
+ "type": "email",
21
+ "label": "Email Address",
22
+ "required": true,
23
+ "placeholder": "your@email.com",
24
+ "helper": "Please enter your email address"
25
+ },
26
+ {
27
+ "name": "rating",
28
+ "type": "select",
29
+ "label": "Overall Rating",
30
+ "required": true,
31
+ "helper": "Tell us how we are doing!",
32
+ "options": [
33
+ {
34
+ "value": "none",
35
+ "label": "Please Choose"
36
+ },
37
+ {
38
+ "value": "excellent",
39
+ "label": "Excellent"
40
+ },
41
+ {
42
+ "value": "good",
43
+ "label": "Good"
44
+ },
45
+ {
46
+ "value": "average",
47
+ "label": "Average"
48
+ },
49
+ {
50
+ "value": "poor",
51
+ "label": "Poor"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "name": "category",
57
+ "type": "select",
58
+ "label": "Category",
59
+ "required": true,
60
+ "placeholder": "Please select a category",
61
+ "helper": "",
62
+ "options": [
63
+ {
64
+ "value": "general",
65
+ "label": "General"
66
+ },
67
+ {
68
+ "value": "bug-report",
69
+ "label": "Bug Report"
70
+ },
71
+ {
72
+ "value": "feature-request",
73
+ "label": "Feature Request"
74
+ },
75
+ {
76
+ "value": "praise",
77
+ "label": "Praise"
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "name": "subject",
83
+ "type": "string",
84
+ "label": "Subject",
85
+ "required": true,
86
+ "placeholder": "Brief summary of your feedback",
87
+ "helper": "",
88
+ "validation": {
89
+ "max": 200
90
+ }
91
+ },
92
+ {
93
+ "name": "message",
94
+ "type": "textarea",
95
+ "label": "Your Feedback",
96
+ "required": true,
97
+ "placeholder": "Please share your thoughts in detail…",
98
+ "helper": "Please share your thoughts in detail…",
99
+ "rows": 4,
100
+ "validation": {
101
+ "min": 10,
102
+ "max": 2000
103
+ }
104
+ }
105
+ ],
106
+ "settings": {
107
+ "submitText": "Submit Feedback",
108
+ "successMessage": "Thank you for your feedback! We appreciate you taking the time.",
109
+ "layout": "stacked",
110
+ "honeypot": true,
111
+ "rateLimitPerMinute": 3
112
+ },
113
+ "actions": {
114
+ "email": {
115
+ "enabled": true,
116
+ "recipients": "",
117
+ "subjectPrefix": "[feedback]"
118
+ },
119
+ "webhook": {
120
+ "enabled": false,
121
+ "url": "",
122
+ "method": "POST"
123
+ },
124
+ "collection": {
125
+ "enabled": true,
126
+ "slug": "feedback"
127
+ }
128
+ },
129
+ "createdAt": "2026-03-17T12:35:44.569Z",
130
+ "updatedAt": "2026-03-17T12:35:44.569Z"
131
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "slug": "notes",
3
+ "title": "Notes",
4
+ "description": "Free-form notes with categories and tags.",
5
+ "fields": [
6
+ {
7
+ "name": "title",
8
+ "type": "string",
9
+ "label": "Title",
10
+ "required": true,
11
+ "placeholder": "Note title"
12
+ },
13
+ {
14
+ "name": "content",
15
+ "type": "textarea",
16
+ "label": "Content",
17
+ "required": true,
18
+ "placeholder": "Write your note here…",
19
+ "rows": 5
20
+ },
21
+ {
22
+ "name": "category",
23
+ "type": "select",
24
+ "label": "Category",
25
+ "required": false,
26
+ "options": [
27
+ {
28
+ "value": "general",
29
+ "label": "General"
30
+ },
31
+ {
32
+ "value": "idea",
33
+ "label": "Idea"
34
+ },
35
+ {
36
+ "value": "reminder",
37
+ "label": "Reminder"
38
+ },
39
+ {
40
+ "value": "reference",
41
+ "label": "Reference"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "name": "tags",
47
+ "type": "string",
48
+ "label": "Tags",
49
+ "required": false,
50
+ "placeholder": "Comma-separated tags",
51
+ "helper": "Separate tags with commas"
52
+ }
53
+ ],
54
+ "settings": {
55
+ "submitText": "Save Note",
56
+ "successMessage": "Note saved successfully.",
57
+ "layout": "stacked",
58
+ "honeypot": true,
59
+ "rateLimitPerMinute": 3
60
+ },
61
+ "actions": {
62
+ "email": {
63
+ "enabled": false,
64
+ "recipients": "",
65
+ "subjectPrefix": "[notes]"
66
+ },
67
+ "webhook": {
68
+ "enabled": false,
69
+ "url": "",
70
+ "method": "POST"
71
+ },
72
+ "collection": {
73
+ "enabled": true,
74
+ "slug": "notes"
75
+ }
76
+ },
77
+ "createdAt": "2026-03-17T12:35:44.569Z",
78
+ "updatedAt": "2026-03-17T12:35:44.569Z"
79
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "slug": "to-do",
3
+ "title": "To-Do",
4
+ "description": "Task tracking with status, priority, and due dates.",
5
+ "fields": [
6
+ {
7
+ "name": "title",
8
+ "type": "string",
9
+ "label": "Title",
10
+ "required": true,
11
+ "placeholder": "Task title"
12
+ },
13
+ {
14
+ "name": "description",
15
+ "type": "textarea",
16
+ "label": "Description",
17
+ "required": false,
18
+ "placeholder": "Task details…",
19
+ "rows": 3
20
+ },
21
+ {
22
+ "name": "status",
23
+ "type": "select",
24
+ "label": "Status",
25
+ "required": true,
26
+ "options": [
27
+ {
28
+ "value": "pending",
29
+ "label": "Pending"
30
+ },
31
+ {
32
+ "value": "in-progress",
33
+ "label": "In Progress"
34
+ },
35
+ {
36
+ "value": "done",
37
+ "label": "Done"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "name": "priority",
43
+ "type": "select",
44
+ "label": "Priority",
45
+ "required": false,
46
+ "options": [
47
+ {
48
+ "value": "low",
49
+ "label": "Low"
50
+ },
51
+ {
52
+ "value": "medium",
53
+ "label": "Medium"
54
+ },
55
+ {
56
+ "value": "high",
57
+ "label": "High"
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ "name": "due_date",
63
+ "type": "date",
64
+ "label": "Due Date",
65
+ "required": false
66
+ },
67
+ {
68
+ "name": "assigned_to",
69
+ "type": "string",
70
+ "label": "Assigned To",
71
+ "required": false,
72
+ "placeholder": "Name or email"
73
+ }
74
+ ],
75
+ "settings": {
76
+ "submitText": "Add Task",
77
+ "successMessage": "Task added successfully.",
78
+ "layout": "stacked",
79
+ "honeypot": true,
80
+ "rateLimitPerMinute": 3
81
+ },
82
+ "actions": {
83
+ "email": {
84
+ "enabled": false,
85
+ "recipients": "",
86
+ "subjectPrefix": "[to-do]"
87
+ },
88
+ "webhook": {
89
+ "enabled": false,
90
+ "url": "",
91
+ "method": "POST"
92
+ },
93
+ "collection": {
94
+ "enabled": true,
95
+ "slug": "to-do"
96
+ }
97
+ },
98
+ "createdAt": "2026-03-17T12:35:44.569Z",
99
+ "updatedAt": "2026-03-17T12:35:44.569Z"
100
+ }
@@ -0,0 +1,40 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title">Application Detail</h1>
4
+ </div>
5
+ <div class="view-actions">
6
+ <a href="#/job-board/applications" class="btn btn-secondary">Back</a>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="view-body">
11
+ <div class="card mb-4">
12
+ <div class="card-header"><h3>Application Info</h3></div>
13
+ <div class="card-body">
14
+ <dl class="dl-grid">
15
+ <dt>Job ID</dt> <dd id="app-job-id">—</dd>
16
+ <dt>Candidate</dt> <dd id="app-candidate">—</dd>
17
+ <dt>Status</dt> <dd id="app-status">—</dd>
18
+ <dt>Applied</dt> <dd id="app-applied">—</dd>
19
+ <dt>CV</dt> <dd id="app-cv">—</dd>
20
+ </dl>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="card mb-4">
25
+ <div class="card-header"><h3>Cover Letter</h3></div>
26
+ <div class="card-body">
27
+ <p id="app-cover-letter" class="whitespace-pre-wrap">—</p>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="card" id="status-section" style="display:none">
32
+ <div class="card-header"><h3>Update Status</h3></div>
33
+ <div class="card-body">
34
+ <div class="form-group">
35
+ <select id="status-select" class="form-control"></select>
36
+ </div>
37
+ <button class="btn btn-primary mt-2" id="btn-update-status">Update Status</button>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,10 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title">Applications</h1>
4
+ <p class="view-subtitle">Review and manage job applications</p>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="view-body">
9
+ <div id="applications-table"></div>
10
+ </div>
@@ -0,0 +1,24 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title">Companies</h1>
4
+ <p class="view-subtitle">Manage companies</p>
5
+ </div>
6
+ <div class="view-actions">
7
+ <button class="btn btn-primary" id="btn-create-company">Add Company</button>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="view-body" id="companies-body">
12
+ <div id="agent-link-section" style="display:none" class="card mb-4">
13
+ <div class="card-header"><h3>Link to a Company</h3></div>
14
+ <div class="card-body">
15
+ <div class="form-group">
16
+ <label>Company ID</label>
17
+ <input type="text" id="link-company-id" class="form-control" placeholder="Enter company ID">
18
+ </div>
19
+ <button class="btn btn-secondary mt-2" id="btn-link-company">Link Company</button>
20
+ </div>
21
+ </div>
22
+
23
+ <div id="companies-table"></div>
24
+ </div>
@@ -0,0 +1,36 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 id="jb-heading" class="view-title">Job Board</h1>
4
+ <p class="view-subtitle">Overview and quick access</p>
5
+ </div>
6
+ </div>
7
+
8
+ <div class="view-body">
9
+ <div class="grid grid-cols-3 gap-4 mb-6">
10
+ <div class="card">
11
+ <div class="card-body text-center">
12
+ <div class="text-3xl font-bold" id="stat-jobs">—</div>
13
+ <div class="text-muted mt-1">Jobs</div>
14
+ </div>
15
+ </div>
16
+ <div class="card">
17
+ <div class="card-body text-center">
18
+ <div class="text-3xl font-bold" id="stat-apps">—</div>
19
+ <div class="text-muted mt-1">Applications</div>
20
+ </div>
21
+ </div>
22
+ <div class="card" id="stat-companies-card">
23
+ <div class="card-body text-center">
24
+ <div class="text-3xl font-bold" id="stat-companies">—</div>
25
+ <div class="text-muted mt-1">Companies</div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="mb-4" id="quick-links"></div>
31
+
32
+ <div id="recent-apps-section">
33
+ <h2 class="section-title">Recent Applications</h2>
34
+ <div id="recent-apps-table"></div>
35
+ </div>
36
+ </div>
@@ -0,0 +1,17 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title" id="view-title">New Job</h1>
4
+ </div>
5
+ <div class="view-actions">
6
+ <a href="#/job-board/jobs" class="btn btn-secondary">Cancel</a>
7
+ <button class="btn btn-primary" id="btn-save">Save Job</button>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="view-body">
12
+ <div class="card">
13
+ <div class="card-body">
14
+ <div id="job-form"></div>
15
+ </div>
16
+ </div>
17
+ </div>
@@ -0,0 +1,15 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title">Jobs</h1>
4
+ <p class="view-subtitle">Manage job listings</p>
5
+ </div>
6
+ <div class="view-actions">
7
+ <a href="#/job-board/jobs/new" class="btn btn-primary" id="btn-post-job">
8
+ <span data-icon="plus"></span> Post Job
9
+ </a>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="view-body">
14
+ <div id="jobs-table"></div>
15
+ </div>
@@ -0,0 +1,17 @@
1
+ <div class="view-header">
2
+ <div>
3
+ <h1 class="view-title">My Profile</h1>
4
+ <p class="view-subtitle">Manage your candidate profile</p>
5
+ </div>
6
+ <div class="view-actions">
7
+ <button class="btn btn-primary" id="btn-save-profile">Save Profile</button>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="view-body" id="profile-body">
12
+ <div class="card">
13
+ <div class="card-body">
14
+ <div id="profile-form"></div>
15
+ </div>
16
+ </div>
17
+ </div>
@@ -0,0 +1,62 @@
1
+ import { getRoleContext } from '../lib/role-context.js';
2
+ import { jbApi } from '../lib/api.js';
3
+
4
+ export default {
5
+ templateUrl: '/plugins/job-board/admin/templates/application-detail.html',
6
+ async onMount($container) {
7
+ const ctx = getRoleContext();
8
+
9
+ const hash = window.location.hash;
10
+ const match = hash.match(/\/job-board\/applications\/([^/]+)/);
11
+ const appId = match ? match[1] : null;
12
+ if (!appId) {
13
+ E.toast('Application ID not found', { type: 'error' });
14
+ return;
15
+ }
16
+
17
+ let application;
18
+ try {
19
+ application = await jbApi.applications.get(appId);
20
+ } catch {
21
+ E.toast('Failed to load application', { type: 'error' });
22
+ return;
23
+ }
24
+
25
+ const d = application.data || {};
26
+ $container.find('#app-job-id').text(d.job_id || '—');
27
+ $container.find('#app-candidate').text(d.candidate_id || '—');
28
+ $container.find('#app-status').text(d.status || '—');
29
+ $container.find('#app-applied').text(d.applied_at ? new Date(d.applied_at).toLocaleString() : '—');
30
+ $container.find('#app-cover-letter').text(d.cover_letter || '—');
31
+ if (d.cv_url) {
32
+ $container.find('#app-cv').html(`<a href="${d.cv_url}" target="_blank">View CV</a>`);
33
+ }
34
+
35
+ // Status update section — company/agent/admin only
36
+ if (!ctx.isCandidate) {
37
+ const statuses = ['submitted', 'reviewed', 'shortlisted', 'interview', 'offered', 'rejected', 'withdrawn'];
38
+ const $sel = $container.find('#status-select');
39
+ statuses.forEach(s => {
40
+ const opt = document.createElement('option');
41
+ opt.value = s;
42
+ opt.textContent = s;
43
+ if (s === d.status) opt.selected = true;
44
+ $sel.get(0)?.appendChild(opt);
45
+ });
46
+ $container.find('#status-section').show();
47
+
48
+ $container.find('#btn-update-status').get(0)?.addEventListener('click', async () => {
49
+ const status = $sel.val();
50
+ try {
51
+ await jbApi.applications.setStatus(appId, status);
52
+ E.toast('Status updated', { type: 'success' });
53
+ $container.find('#app-status').text(status);
54
+ } catch (err) {
55
+ E.toast(err.message || 'Failed to update status', { type: 'error' });
56
+ }
57
+ });
58
+ }
59
+
60
+ I.scan($container.get(0));
61
+ }
62
+ };
@@ -0,0 +1,47 @@
1
+ import { getRoleContext } from '../lib/role-context.js';
2
+ import { jbApi } from '../lib/api.js';
3
+
4
+ export default {
5
+ templateUrl: '/plugins/job-board/admin/templates/applications.html',
6
+ async onMount($container) {
7
+ const ctx = getRoleContext();
8
+
9
+ let applications = [];
10
+ try {
11
+ const res = await jbApi.applications.list();
12
+ applications = Array.isArray(res) ? res : (res?.entries || []);
13
+ } catch {
14
+ E.toast('Failed to load applications', { type: 'error' });
15
+ return;
16
+ }
17
+
18
+ const statusBadge = (s) => {
19
+ const map = {
20
+ submitted: 'badge-info',
21
+ reviewed: 'badge-secondary',
22
+ shortlisted: 'badge-warning',
23
+ interview: 'badge-primary',
24
+ offered: 'badge-success',
25
+ rejected: 'badge-danger',
26
+ withdrawn: 'badge-secondary'
27
+ };
28
+ return `<span class="badge ${map[s] || 'badge-secondary'}">${s || '—'}</span>`;
29
+ };
30
+
31
+ const columns = [
32
+ { key: 'data.company_id', label: 'Company' },
33
+ { key: 'data.job_id', label: 'Job ID' },
34
+ { key: 'data.candidate_id', label: 'Candidate' },
35
+ { key: 'data.status', label: 'Status', render: v => statusBadge(v) },
36
+ { key: 'data.applied_at', label: 'Applied', render: v => v ? new Date(v).toLocaleDateString() : '—' },
37
+ {
38
+ label: 'Actions',
39
+ render: (_, row) => `<a href="#/job-board/applications/${row.id}" class="btn btn-sm btn-secondary">View</a>`
40
+ }
41
+ ];
42
+
43
+ T.create($container.find('#applications-table').get(0), { data: applications, columns });
44
+
45
+ I.scan($container.get(0));
46
+ }
47
+ };