@zeyos/client 0.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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/agents/README.md +66 -0
- package/agents/shared/business-app-benchmarks.md +111 -0
- package/agents/shared/zeyos-entity-map.md +142 -0
- package/agents/shared/zeyos-entity-reference.md +570 -0
- package/agents/shared/zeyos-query-patterns.md +89 -0
- package/agents/zeyos-account-intelligence/SKILL.md +34 -0
- package/agents/zeyos-account-intelligence/agents/openai.yaml +4 -0
- package/agents/zeyos-account-intelligence/references/workflows.md +84 -0
- package/agents/zeyos-billing-insights/SKILL.md +41 -0
- package/agents/zeyos-billing-insights/agents/openai.yaml +4 -0
- package/agents/zeyos-billing-insights/references/workflows.md +106 -0
- package/agents/zeyos-campaign-and-outreach/SKILL.md +44 -0
- package/agents/zeyos-campaign-and-outreach/agents/openai.yaml +4 -0
- package/agents/zeyos-campaign-and-outreach/references/workflows.md +100 -0
- package/agents/zeyos-collaboration-and-activity/SKILL.md +37 -0
- package/agents/zeyos-collaboration-and-activity/agents/openai.yaml +4 -0
- package/agents/zeyos-collaboration-and-activity/references/workflows.md +104 -0
- package/agents/zeyos-collections-and-dunning/SKILL.md +46 -0
- package/agents/zeyos-collections-and-dunning/agents/openai.yaml +4 -0
- package/agents/zeyos-collections-and-dunning/references/workflows.md +132 -0
- package/agents/zeyos-commerce-and-inventory/SKILL.md +38 -0
- package/agents/zeyos-commerce-and-inventory/agents/openai.yaml +4 -0
- package/agents/zeyos-commerce-and-inventory/references/workflows.md +101 -0
- package/agents/zeyos-mail-operations/SKILL.md +35 -0
- package/agents/zeyos-mail-operations/agents/openai.yaml +4 -0
- package/agents/zeyos-mail-operations/references/workflows.md +110 -0
- package/agents/zeyos-notes-and-sops/SKILL.md +31 -0
- package/agents/zeyos-notes-and-sops/agents/openai.yaml +4 -0
- package/agents/zeyos-notes-and-sops/references/workflows.md +85 -0
- package/agents/zeyos-platform-and-schema/SKILL.md +37 -0
- package/agents/zeyos-platform-and-schema/agents/openai.yaml +4 -0
- package/agents/zeyos-platform-and-schema/references/workflows.md +97 -0
- package/agents/zeyos-work-management/SKILL.md +45 -0
- package/agents/zeyos-work-management/agents/openai.yaml +4 -0
- package/agents/zeyos-work-management/references/workflows.md +148 -0
- package/docs/01-api-reference/01-data-retrieval.md +601 -0
- package/docs/01-api-reference/02-authentication.md +288 -0
- package/docs/01-api-reference/03-resources.md +270 -0
- package/docs/01-api-reference/04-schema.md +539 -0
- package/docs/01-api-reference/_category_.json +9 -0
- package/docs/02-javascript-client/01-getting-started.md +146 -0
- package/docs/02-javascript-client/02-authentication.md +287 -0
- package/docs/02-javascript-client/03-making-requests.md +572 -0
- package/docs/02-javascript-client/04-practical-guide.md +348 -0
- package/docs/02-javascript-client/_category_.json +9 -0
- package/docs/03-cli/01-getting-started.md +219 -0
- package/docs/03-cli/02-commands.md +407 -0
- package/docs/03-cli/03-configuration.md +220 -0
- package/docs/03-cli/_category_.json +9 -0
- package/docs/04-agent-workflows/00-coding-agents.md +35 -0
- package/docs/04-agent-workflows/01-agent-quickstart.md +147 -0
- package/docs/04-agent-workflows/02-agent-recipes.md +109 -0
- package/docs/04-agent-workflows/03-cli-coverage-and-escalation.md +65 -0
- package/docs/04-agent-workflows/_category_.json +9 -0
- package/docs/04-sample-apps/01-kanban.md +89 -0
- package/docs/04-sample-apps/02-crm.md +81 -0
- package/docs/04-sample-apps/03-dashboard.md +80 -0
- package/docs/04-sample-apps/_category_.json +9 -0
- package/docs/05-tutorials/00-application-developers.md +43 -0
- package/docs/05-tutorials/01-integration-architecture.md +60 -0
- package/docs/05-tutorials/02-build-your-own-zeyos-frontend.md +517 -0
- package/docs/05-tutorials/03-server-side-integrations.md +185 -0
- package/docs/05-tutorials/_category_.json +9 -0
- package/docs/intro.md +197 -0
- package/openapi/api.json +24308 -0
- package/openapi/auth.json +415 -0
- package/openapi/dbref.json +56223 -0
- package/openapi/oauth2.json +781 -0
- package/openapi/sdk.json +949 -0
- package/openapi/views.txt +642 -0
- package/package.json +49 -0
- package/samples/crm/README.md +28 -0
- package/samples/crm/index.html +327 -0
- package/samples/crm/js/api.js +208 -0
- package/samples/crm/js/auth.js +61 -0
- package/samples/crm/js/main.js +545 -0
- package/samples/crm/js/state.js +90 -0
- package/samples/crm/js/ui.js +51 -0
- package/samples/dashboard/README.md +28 -0
- package/samples/dashboard/index.html +280 -0
- package/samples/dashboard/js/api.js +197 -0
- package/samples/dashboard/js/auth.js +59 -0
- package/samples/dashboard/js/main.js +382 -0
- package/samples/dashboard/js/state.js +81 -0
- package/samples/dashboard/js/ui.js +48 -0
- package/samples/kanban/README.md +28 -0
- package/samples/kanban/index.html +263 -0
- package/samples/kanban/js/api.js +152 -0
- package/samples/kanban/js/auth.js +59 -0
- package/samples/kanban/js/constants.js +40 -0
- package/samples/kanban/js/kanban.js +246 -0
- package/samples/kanban/js/main.js +362 -0
- package/samples/kanban/js/modals.js +474 -0
- package/samples/kanban/js/settings.js +82 -0
- package/samples/kanban/js/state.js +118 -0
- package/samples/kanban/js/ui.js +49 -0
- package/scripts/generate-client.mjs +344 -0
- package/src/generated/operations.js +9772 -0
- package/src/generated/schema.js +8982 -0
- package/src/index.js +85 -0
- package/src/runtime/client.js +1208 -0
- package/src/runtime/error.js +29 -0
- package/src/runtime/http.js +174 -0
- package/src/runtime/request-shape.js +35 -0
- package/src/runtime/schema.js +206 -0
- package/src/runtime/suggest.js +74 -0
- package/src/runtime/token-store.js +105 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ZeyOS Dashboard Sample
|
|
2
|
+
|
|
3
|
+
This directory contains the static dashboard sample application.
|
|
4
|
+
|
|
5
|
+
The canonical documentation is:
|
|
6
|
+
|
|
7
|
+
- [Dashboard sample docs](../../docs/04-sample-apps/03-dashboard.md)
|
|
8
|
+
|
|
9
|
+
## Quick Run
|
|
10
|
+
|
|
11
|
+
Serve the repository root with any static file server:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd /path/to/zeyos/client
|
|
15
|
+
python3 -m http.server 8080
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then open:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
http://localhost:8080/samples/dashboard/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Configure the sample via `data-zeyos-*` attributes in [`index.html`](./index.html) or through the `window.ZeyOS` console API described in the docs.
|
|
25
|
+
|
|
26
|
+
For localhost, token mode is usually the reliable path. Session mode only works from the same origin or when the ZeyOS instance allows credentialed CORS.
|
|
27
|
+
|
|
28
|
+
> **Note:** The sample must be served from the **repository root** (the directory containing `src/`), because the client is imported via `../../../src/index.js`. Copying the `samples/` folder in isolation will break that import. Also, do **not** open `index.html` directly via the `file://` protocol — browsers block ES module relative imports under `file://`. Always use a local static server as shown above.
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ZeyOS Dashboard</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<style>
|
|
9
|
+
/* Subtle fade-in for cards */
|
|
10
|
+
.fade-in { animation: fadeIn 0.4s ease-out; }
|
|
11
|
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
|
|
15
|
+
<!--
|
|
16
|
+
Configuration
|
|
17
|
+
|
|
18
|
+
Set data-zeyos-url to your ZeyOS instance URL.
|
|
19
|
+
Optionally provide tokens obtained via the CLI (zeyos login).
|
|
20
|
+
|
|
21
|
+
Alternatively, use the browser console:
|
|
22
|
+
ZeyOS.setUrl('https://cloud.zeyos.com/demo/')
|
|
23
|
+
ZeyOS.setToken('your-access-token')
|
|
24
|
+
ZeyOS.reconnect()
|
|
25
|
+
|
|
26
|
+
Session mode only works from the same origin or with credentialed CORS
|
|
27
|
+
enabled on the ZeyOS instance. Use token mode for localhost.
|
|
28
|
+
-->
|
|
29
|
+
<body class="bg-slate-100 min-h-screen font-sans antialiased"
|
|
30
|
+
data-zeyos-url=""
|
|
31
|
+
data-zeyos-accesstoken=""
|
|
32
|
+
data-zeyos-refreshtoken="">
|
|
33
|
+
|
|
34
|
+
<!-- == Connection Screen ==================================================== -->
|
|
35
|
+
<div id="connection-screen" class="hidden min-h-screen flex items-center justify-center p-4">
|
|
36
|
+
<div class="bg-white rounded-2xl shadow-xl p-8 w-full max-w-md">
|
|
37
|
+
|
|
38
|
+
<!-- Logo / title -->
|
|
39
|
+
<div class="text-center mb-6">
|
|
40
|
+
<div class="w-14 h-14 rounded-2xl bg-blue-600 flex items-center justify-center mx-auto mb-4">
|
|
41
|
+
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
42
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
43
|
+
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
44
|
+
</svg>
|
|
45
|
+
</div>
|
|
46
|
+
<h1 class="text-2xl font-bold text-slate-900">ZeyOS Dashboard</h1>
|
|
47
|
+
<p id="connection-message" class="text-slate-500 text-sm mt-1">Not connected</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Setup instructions -->
|
|
51
|
+
<div class="space-y-4 text-sm text-slate-600">
|
|
52
|
+
|
|
53
|
+
<div class="p-4 bg-blue-50 border border-blue-200 rounded-xl">
|
|
54
|
+
<h3 class="font-semibold text-blue-800 mb-2">Option 1 — Body Attributes</h3>
|
|
55
|
+
<p class="mb-2 leading-relaxed">Set connection details directly in <code class="text-xs bg-blue-100 px-1 py-0.5 rounded">index.html</code>:</p>
|
|
56
|
+
<pre class="text-xs bg-white p-3 rounded-lg border border-blue-100 overflow-x-auto leading-relaxed"><code><body
|
|
57
|
+
data-zeyos-url="https://cloud.zeyos.com/demo/"
|
|
58
|
+
data-zeyos-accesstoken="your-token"></code></pre>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="p-4 bg-emerald-50 border border-emerald-200 rounded-xl">
|
|
62
|
+
<h3 class="font-semibold text-emerald-800 mb-2">Option 2 — Console API</h3>
|
|
63
|
+
<p class="mb-2 leading-relaxed">Open DevTools (<kbd class="text-xs bg-emerald-100 px-1.5 py-0.5 rounded font-mono">F12</kbd>) and run:</p>
|
|
64
|
+
<pre class="text-xs bg-white p-3 rounded-lg border border-emerald-100 overflow-x-auto leading-relaxed"><code>ZeyOS.setUrl('https://cloud.zeyos.com/demo/')
|
|
65
|
+
ZeyOS.setToken('your-access-token')
|
|
66
|
+
ZeyOS.reconnect()</code></pre>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="p-4 bg-amber-50 border border-amber-200 rounded-xl">
|
|
70
|
+
<h3 class="font-semibold text-amber-800 mb-2">Option 3 — Session Mode</h3>
|
|
71
|
+
<p class="leading-relaxed">
|
|
72
|
+
Session mode only works from the same origin or when the ZeyOS instance
|
|
73
|
+
allows credentialed CORS. For localhost demos, use token mode.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="p-3 bg-slate-50 border border-slate-200 rounded-xl text-xs text-slate-500 space-y-1.5">
|
|
78
|
+
<p>
|
|
79
|
+
<strong>Tip:</strong> Use the CLI to obtain a token with
|
|
80
|
+
<code class="bg-slate-100 px-1 py-0.5 rounded font-mono">zeyos login</code>, then explicitly print one with
|
|
81
|
+
<code class="bg-slate-100 px-1 py-0.5 rounded font-mono">zeyos whoami --show-token --json</code>
|
|
82
|
+
</p>
|
|
83
|
+
<p>
|
|
84
|
+
<strong>Debug:</strong> Run <code class="bg-slate-100 px-1 py-0.5 rounded font-mono">ZeyOS.status()</code>
|
|
85
|
+
in the console to see the current config.
|
|
86
|
+
</p>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<!-- == App Shell ============================================================ -->
|
|
94
|
+
<div id="app-shell" class="hidden min-h-screen flex flex-col">
|
|
95
|
+
|
|
96
|
+
<!-- Navbar -->
|
|
97
|
+
<header class="bg-white border-b border-slate-200 px-4 lg:px-6 py-2.5 flex items-center gap-3 flex-shrink-0 shadow-sm">
|
|
98
|
+
|
|
99
|
+
<!-- Logo -->
|
|
100
|
+
<div class="flex items-center gap-2 mr-2">
|
|
101
|
+
<div class="w-7 h-7 rounded-lg bg-blue-600 flex items-center justify-center">
|
|
102
|
+
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
103
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
104
|
+
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
105
|
+
</svg>
|
|
106
|
+
</div>
|
|
107
|
+
<span class="font-semibold text-slate-800 hidden sm:inline">ZeyOS Dashboard</span>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="flex-1"></div>
|
|
111
|
+
|
|
112
|
+
<!-- Reload button -->
|
|
113
|
+
<button id="btn-reload" title="Refresh data"
|
|
114
|
+
class="p-2 rounded-lg hover:bg-slate-100 text-slate-500 hover:text-slate-700 transition-colors">
|
|
115
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
116
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
117
|
+
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
120
|
+
|
|
121
|
+
<!-- Logout -->
|
|
122
|
+
<button id="btn-logout" title="Log out"
|
|
123
|
+
class="p-2 rounded-lg hover:bg-slate-100 text-slate-500 hover:text-red-500 transition-colors">
|
|
124
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
125
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
126
|
+
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
|
127
|
+
</svg>
|
|
128
|
+
</button>
|
|
129
|
+
|
|
130
|
+
</header>
|
|
131
|
+
|
|
132
|
+
<!-- Dashboard Content -->
|
|
133
|
+
<main class="flex-1 overflow-y-auto p-4 lg:p-6">
|
|
134
|
+
<div class="max-w-7xl mx-auto space-y-6">
|
|
135
|
+
|
|
136
|
+
<!-- KPI Cards Row -->
|
|
137
|
+
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
138
|
+
|
|
139
|
+
<!-- Total Tickets -->
|
|
140
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-5">
|
|
141
|
+
<div class="flex items-center gap-3 mb-3">
|
|
142
|
+
<div class="w-10 h-10 rounded-lg bg-blue-50 flex items-center justify-center flex-shrink-0">
|
|
143
|
+
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
144
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
145
|
+
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
|
146
|
+
</svg>
|
|
147
|
+
</div>
|
|
148
|
+
<span class="text-xs font-medium text-slate-500 uppercase tracking-wide">Total Tickets</span>
|
|
149
|
+
</div>
|
|
150
|
+
<p id="kpi-total-tickets" class="text-3xl font-bold text-slate-800">--</p>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Active Tickets -->
|
|
154
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-5" style="animation-delay:0.05s">
|
|
155
|
+
<div class="flex items-center gap-3 mb-3">
|
|
156
|
+
<div class="w-10 h-10 rounded-lg bg-emerald-50 flex items-center justify-center flex-shrink-0">
|
|
157
|
+
<svg class="w-5 h-5 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
158
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
159
|
+
d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
160
|
+
</svg>
|
|
161
|
+
</div>
|
|
162
|
+
<span class="text-xs font-medium text-slate-500 uppercase tracking-wide">Active Tickets</span>
|
|
163
|
+
</div>
|
|
164
|
+
<p id="kpi-active-tickets" class="text-3xl font-bold text-slate-800">--</p>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<!-- Overdue Tickets -->
|
|
168
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-5" style="animation-delay:0.1s">
|
|
169
|
+
<div class="flex items-center gap-3 mb-3">
|
|
170
|
+
<div class="w-10 h-10 rounded-lg bg-red-50 flex items-center justify-center flex-shrink-0">
|
|
171
|
+
<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
172
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
173
|
+
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
174
|
+
</svg>
|
|
175
|
+
</div>
|
|
176
|
+
<span class="text-xs font-medium text-slate-500 uppercase tracking-wide">Overdue</span>
|
|
177
|
+
</div>
|
|
178
|
+
<p id="kpi-overdue-tickets" class="text-3xl font-bold text-red-600">--</p>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<!-- Total Accounts -->
|
|
182
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-5" style="animation-delay:0.15s">
|
|
183
|
+
<div class="flex items-center gap-3 mb-3">
|
|
184
|
+
<div class="w-10 h-10 rounded-lg bg-violet-50 flex items-center justify-center flex-shrink-0">
|
|
185
|
+
<svg class="w-5 h-5 text-violet-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
186
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
187
|
+
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
|
188
|
+
</svg>
|
|
189
|
+
</div>
|
|
190
|
+
<span class="text-xs font-medium text-slate-500 uppercase tracking-wide">Total Accounts</span>
|
|
191
|
+
</div>
|
|
192
|
+
<p id="kpi-total-accounts" class="text-3xl font-bold text-slate-800">--</p>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<!-- Status Distribution Chart -->
|
|
198
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 p-5" style="animation-delay:0.2s">
|
|
199
|
+
<h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wide mb-4">Ticket Status Distribution</h2>
|
|
200
|
+
<div id="status-chart" class="space-y-2">
|
|
201
|
+
<p class="text-sm text-slate-400 italic">Loading...</p>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<!-- Two-Column Tables -->
|
|
206
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
207
|
+
|
|
208
|
+
<!-- Recent Tickets -->
|
|
209
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden" style="animation-delay:0.25s">
|
|
210
|
+
<div class="px-5 py-4 border-b border-slate-100">
|
|
211
|
+
<h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wide">Recent Tickets</h2>
|
|
212
|
+
<p class="text-xs text-slate-400 mt-0.5">Last 10 modified</p>
|
|
213
|
+
</div>
|
|
214
|
+
<div class="overflow-x-auto">
|
|
215
|
+
<table class="w-full">
|
|
216
|
+
<thead>
|
|
217
|
+
<tr class="bg-slate-50/80 text-left">
|
|
218
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">ID</th>
|
|
219
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Name</th>
|
|
220
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Status</th>
|
|
221
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Priority</th>
|
|
222
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Due Date</th>
|
|
223
|
+
</tr>
|
|
224
|
+
</thead>
|
|
225
|
+
<tbody id="recent-tickets-body">
|
|
226
|
+
<tr><td colspan="5" class="px-4 py-8 text-center text-sm text-slate-400 italic">Loading...</td></tr>
|
|
227
|
+
</tbody>
|
|
228
|
+
</table>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<!-- Recent Accounts -->
|
|
233
|
+
<div class="fade-in bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden" style="animation-delay:0.3s">
|
|
234
|
+
<div class="px-5 py-4 border-b border-slate-100">
|
|
235
|
+
<h2 class="text-sm font-semibold text-slate-700 uppercase tracking-wide">Recent Accounts</h2>
|
|
236
|
+
<p class="text-xs text-slate-400 mt-0.5">Last 10 modified</p>
|
|
237
|
+
</div>
|
|
238
|
+
<div class="overflow-x-auto">
|
|
239
|
+
<table class="w-full">
|
|
240
|
+
<thead>
|
|
241
|
+
<tr class="bg-slate-50/80 text-left">
|
|
242
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">ID</th>
|
|
243
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Name</th>
|
|
244
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Type</th>
|
|
245
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">City</th>
|
|
246
|
+
<th class="px-4 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wider">Assigned To</th>
|
|
247
|
+
</tr>
|
|
248
|
+
</thead>
|
|
249
|
+
<tbody id="recent-accounts-body">
|
|
250
|
+
<tr><td colspan="5" class="px-4 py-8 text-center text-sm text-slate-400 italic">Loading...</td></tr>
|
|
251
|
+
</tbody>
|
|
252
|
+
</table>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
</div>
|
|
259
|
+
</main>
|
|
260
|
+
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<!-- == Toast Container ====================================================== -->
|
|
264
|
+
<div id="toast-container"
|
|
265
|
+
class="fixed bottom-5 right-5 z-50 flex flex-col gap-2 pointer-events-none">
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<!-- == Loading Overlay ====================================================== -->
|
|
269
|
+
<div id="loading-overlay"
|
|
270
|
+
class="hidden fixed inset-0 bg-white/60 backdrop-blur-sm z-50 flex items-center justify-center">
|
|
271
|
+
<div class="flex flex-col items-center gap-3">
|
|
272
|
+
<div class="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
|
|
273
|
+
<span class="text-sm text-slate-600 font-medium">Loading…</span>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<script type="module" src="./js/main.js"></script>
|
|
278
|
+
|
|
279
|
+
</body>
|
|
280
|
+
</html>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZeyOS API client singleton + dashboard-specific helper functions.
|
|
3
|
+
* Supports two initialization modes:
|
|
4
|
+
* - Token mode: uses pre-obtained browser tokens via MemoryTokenStore
|
|
5
|
+
* - Session mode: uses browser session cookies
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
createZeyosClient,
|
|
9
|
+
MemoryTokenStore,
|
|
10
|
+
normalizeCountResult,
|
|
11
|
+
normalizeListResult
|
|
12
|
+
} from '../../../src/index.js';
|
|
13
|
+
import { loadTokens, saveTokens } from './state.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {null|boolean|number|string|JsonValue[]|Record<string, JsonValue>} JsonValue
|
|
17
|
+
* @typedef {Record<string, JsonValue>} ZeyosRecord
|
|
18
|
+
* @typedef {Record<string, JsonValue>} TicketFilters
|
|
19
|
+
* @typedef {{ ID?: number|string, ticketnum?: string|number, name?: string, status?: number, priority?: number, duedate?: number|string|null, lastmodified?: number|string|null }} DashboardTicket
|
|
20
|
+
* @typedef {{ ID?: number|string, lastname?: string, firstname?: string|null, type?: number|string, contact?: ZeyosRecord, assigneduser?: ZeyosRecord, lastmodified?: number|string|null }} DashboardAccount
|
|
21
|
+
* @typedef {{ status: number, label: string, count: number, color: string }} StatusCount
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export let client = null;
|
|
25
|
+
export let tokenStore = null;
|
|
26
|
+
|
|
27
|
+
// -- Status labels & colors ---------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export const STATUS_LABELS = [
|
|
30
|
+
'Not Started', // 0
|
|
31
|
+
'Awaiting Acceptance', // 1
|
|
32
|
+
'Accepted', // 2
|
|
33
|
+
'Rejected', // 3
|
|
34
|
+
'Active', // 4
|
|
35
|
+
'Inactive', // 5
|
|
36
|
+
'Feedback Required', // 6
|
|
37
|
+
'Testing', // 7
|
|
38
|
+
'Cancelled', // 8
|
|
39
|
+
'Completed', // 9
|
|
40
|
+
'Failed', // 10
|
|
41
|
+
'Booked', // 11
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export const STATUS_COLORS = [
|
|
45
|
+
'#94a3b8', // 0 Not Started
|
|
46
|
+
'#f59e0b', // 1 Awaiting Acceptance
|
|
47
|
+
'#22c55e', // 2 Accepted
|
|
48
|
+
'#ef4444', // 3 Rejected
|
|
49
|
+
'#3b82f6', // 4 Active
|
|
50
|
+
'#9ca3af', // 5 Inactive
|
|
51
|
+
'#f97316', // 6 Feedback Required
|
|
52
|
+
'#a855f7', // 7 Testing
|
|
53
|
+
'#fb7185', // 8 Cancelled
|
|
54
|
+
'#10b981', // 9 Completed
|
|
55
|
+
'#dc2626', // 10 Failed
|
|
56
|
+
'#14b8a6', // 11 Booked
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// -- Client Initialization ----------------------------------------------------
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize client in token/OAuth mode.
|
|
63
|
+
*/
|
|
64
|
+
export function initTokenClient(url) {
|
|
65
|
+
const stored = loadTokens();
|
|
66
|
+
tokenStore = new MemoryTokenStore(stored ?? undefined);
|
|
67
|
+
|
|
68
|
+
client = createZeyosClient({
|
|
69
|
+
platform: url,
|
|
70
|
+
auth: {
|
|
71
|
+
mode: 'oauth',
|
|
72
|
+
oauth: {
|
|
73
|
+
tokenStore,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return client;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Initialize client in session mode.
|
|
82
|
+
*/
|
|
83
|
+
export function initSessionClient(url) {
|
|
84
|
+
tokenStore = null;
|
|
85
|
+
|
|
86
|
+
client = createZeyosClient({
|
|
87
|
+
platform: url,
|
|
88
|
+
auth: {
|
|
89
|
+
mode: 'session',
|
|
90
|
+
session: {
|
|
91
|
+
enabled: true,
|
|
92
|
+
credentials: 'include',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
return client;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Persist the current token set back to localStorage (no-op in session mode). */
|
|
100
|
+
export async function syncTokens() {
|
|
101
|
+
if (!tokenStore) return;
|
|
102
|
+
try {
|
|
103
|
+
const ts = await client.auth.getTokenSet();
|
|
104
|
+
if (ts?.accessToken) {
|
|
105
|
+
saveTokens({
|
|
106
|
+
accessToken: ts.accessToken,
|
|
107
|
+
refreshToken: ts.refreshToken,
|
|
108
|
+
expiresAt: ts.expiresAt,
|
|
109
|
+
refreshTokenExpiresAt: ts.refreshTokenExpiresAt,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// non-critical
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// -- Tickets ------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Count tickets matching optional filters.
|
|
121
|
+
* Uses count:true to get total without fetching records.
|
|
122
|
+
* @param {TicketFilters} [extraFilters] - additional filter conditions
|
|
123
|
+
* @returns {Promise<number>}
|
|
124
|
+
*/
|
|
125
|
+
export async function countTickets(extraFilters = {}) {
|
|
126
|
+
const result = await client.api.listTickets({
|
|
127
|
+
filters: { visibility: 0, ...extraFilters },
|
|
128
|
+
count: true,
|
|
129
|
+
});
|
|
130
|
+
await syncTokens();
|
|
131
|
+
return normalizeCountResult(result);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Fetch the most recently modified tickets.
|
|
136
|
+
* @param {number} [limit=10]
|
|
137
|
+
* @returns {Promise<DashboardTicket[]>}
|
|
138
|
+
*/
|
|
139
|
+
export async function fetchRecentTickets(limit = 10) {
|
|
140
|
+
const result = await client.api.listTickets({
|
|
141
|
+
fields: ['ID', 'ticketnum', 'name', 'status', 'priority', 'duedate', 'lastmodified'],
|
|
142
|
+
filters: { visibility: 0 },
|
|
143
|
+
sort: ['-lastmodified'],
|
|
144
|
+
limit,
|
|
145
|
+
});
|
|
146
|
+
await syncTokens();
|
|
147
|
+
return normalizeListResult(result).data;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get ticket counts grouped by status.
|
|
152
|
+
* Queries each status value (0-11) with count:true.
|
|
153
|
+
* @returns {Promise<StatusCount[]>}
|
|
154
|
+
*/
|
|
155
|
+
export async function fetchTicketsByStatus() {
|
|
156
|
+
const promises = STATUS_LABELS.map((label, status) =>
|
|
157
|
+
countTickets({ status }).then(count => ({
|
|
158
|
+
status,
|
|
159
|
+
label,
|
|
160
|
+
count,
|
|
161
|
+
color: STATUS_COLORS[status],
|
|
162
|
+
}))
|
|
163
|
+
);
|
|
164
|
+
const results = await Promise.all(promises);
|
|
165
|
+
return results;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// -- Accounts -----------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Count total accounts.
|
|
172
|
+
* @returns {Promise<number>}
|
|
173
|
+
*/
|
|
174
|
+
export async function countAccounts() {
|
|
175
|
+
const result = await client.api.listAccounts({
|
|
176
|
+
filters: { visibility: 0 },
|
|
177
|
+
count: true,
|
|
178
|
+
});
|
|
179
|
+
await syncTokens();
|
|
180
|
+
return normalizeCountResult(result);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Fetch the most recently modified accounts with dot-notation joins.
|
|
185
|
+
* @param {number} [limit=10]
|
|
186
|
+
* @returns {Promise<DashboardAccount[]>}
|
|
187
|
+
*/
|
|
188
|
+
export async function fetchRecentAccounts(limit = 10) {
|
|
189
|
+
const result = await client.api.listAccounts({
|
|
190
|
+
fields: ['ID', 'lastname', 'firstname', 'type', 'contact.city', 'contact.email', 'assigneduser.name', 'lastmodified'],
|
|
191
|
+
filters: { visibility: 0 },
|
|
192
|
+
sort: ['-lastmodified'],
|
|
193
|
+
limit,
|
|
194
|
+
});
|
|
195
|
+
await syncTokens();
|
|
196
|
+
return normalizeListResult(result).data;
|
|
197
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication helpers.
|
|
3
|
+
* Supports two modes:
|
|
4
|
+
* - Token mode: pre-obtained browser tokens provided via body attributes or console API
|
|
5
|
+
* - Session mode: browser cookies sent to ZeyOS (user already logged in)
|
|
6
|
+
*/
|
|
7
|
+
import { client } from './api.js';
|
|
8
|
+
import { loadTokens, clearTokens } from './state.js';
|
|
9
|
+
|
|
10
|
+
// -- Public API ---------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Probe the ZeyOS instance for an active browser session.
|
|
14
|
+
* Calls GET {url}/oauth2/v1/userinfo with credentials:'include' to
|
|
15
|
+
* check whether the user is already logged in via a session cookie.
|
|
16
|
+
* Returns the user info object on success, null otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export async function trySessionAuth(url) {
|
|
19
|
+
try {
|
|
20
|
+
const endpoint = `${url.replace(/\/+$/, '')}/oauth2/v1/userinfo`;
|
|
21
|
+
const res = await fetch(endpoint, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
credentials: 'include',
|
|
24
|
+
headers: { 'Accept': 'application/json' },
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) return null;
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
// A valid userinfo response must have a subject (user identifier)
|
|
29
|
+
return (data && (data.sub || data.ID)) ? data : null;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns true if a valid (non-expired) access token exists in localStorage.
|
|
37
|
+
*/
|
|
38
|
+
export function isAuthenticated() {
|
|
39
|
+
const tokens = loadTokens();
|
|
40
|
+
if (!tokens?.accessToken) return false;
|
|
41
|
+
if (!tokens.expiresAt) return true;
|
|
42
|
+
// Token still valid with 2-minute buffer (expiresAt is Unix seconds)
|
|
43
|
+
return Date.now() / 1000 < tokens.expiresAt - 120;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Clear tokens and revoke the access token if possible.
|
|
48
|
+
*/
|
|
49
|
+
export async function logout() {
|
|
50
|
+
const tokens = loadTokens();
|
|
51
|
+
if (tokens?.accessToken && client) {
|
|
52
|
+
try {
|
|
53
|
+
await client.oauth2.revokeToken({ token: tokens.accessToken });
|
|
54
|
+
} catch {
|
|
55
|
+
// best-effort revocation
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
clearTokens();
|
|
59
|
+
}
|