@eventcatalog/core 5.0.0-beta.27 → 5.0.0-beta.29
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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +57 -3
- package/dist/analytics/log-build.d.cts +2 -2
- package/dist/analytics/log-build.d.ts +2 -2
- package/dist/analytics/log-build.js +5 -4
- package/dist/analytics/studio-usage.cjs +90 -0
- package/dist/analytics/studio-usage.d.cts +5 -0
- package/dist/analytics/studio-usage.d.ts +5 -0
- package/dist/analytics/studio-usage.js +10 -0
- package/dist/{chunk-ILPH363P.js → chunk-34VU2X7H.js} +1 -1
- package/dist/{chunk-I6CBMGUM.js → chunk-3RDV4HXA.js} +1 -1
- package/dist/{chunk-P5IFIKG6.js → chunk-4IREUUHE.js} +12 -4
- package/dist/{chunk-AWOO63ME.js → chunk-AD7CL772.js} +11 -4
- package/dist/{chunk-EFBDR7JJ.js → chunk-B4OOUG5I.js} +1 -1
- package/dist/chunk-CA6WCQ2T.js +54 -0
- package/dist/{chunk-QJ7KKN2D.js → chunk-OX6TSVHY.js} +1 -1
- package/dist/{chunk-YGUOLT7F.js → chunk-ZK7Q7XDV.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +246 -186
- package/dist/eventcatalog.js +12 -10
- package/dist/federation/federate.js +3 -3
- package/dist/federation/source-provider.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/dist/utils/license-status.cjs +11 -4
- package/dist/utils/license-status.d.cts +3 -2
- package/dist/utils/license-status.d.ts +3 -2
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +19 -2
- package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
- package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
- package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
- package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
- package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
- package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
- package/eventcatalog/src/components/Settings/Row.tsx +7 -4
- package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
- package/eventcatalog/src/components/TrialBadge.astro +20 -9
- package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
- package/eventcatalog/src/pages/settings/license.astro +9 -1
- package/eventcatalog/src/utils/license.ts +1 -1
- package/eventcatalog/src/utils/trial.ts +13 -0
- package/package.json +3 -3
- package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
|
@@ -179,193 +179,198 @@ export const GeneralSettingsForm = ({ canEdit, initial, apiBase }: Props) => {
|
|
|
179
179
|
};
|
|
180
180
|
|
|
181
181
|
return (
|
|
182
|
-
|
|
182
|
+
<>
|
|
183
183
|
<Toaster richColors closeButton position="bottom-right" theme="system" />
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
</div>
|
|
188
|
-
)}
|
|
189
|
-
|
|
190
|
-
<Row
|
|
191
|
-
title="Theme"
|
|
192
|
-
description="Pick a built-in colour theme. Selection updates the preview live; click Save to persist."
|
|
193
|
-
canEdit={canEdit}
|
|
194
|
-
dirty={dirtyKeys.has('theme')}
|
|
195
|
-
saving={savingKey === 'theme'}
|
|
196
|
-
onSave={() => saveRow('theme')}
|
|
197
|
-
error={errors.theme}
|
|
184
|
+
<form
|
|
185
|
+
onSubmit={(e) => e.preventDefault()}
|
|
186
|
+
className="divide-y divide-[rgb(var(--ec-page-border))] border-t border-[rgb(var(--ec-page-border))]"
|
|
198
187
|
>
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
style={{ background: 'rgba(255,255,255,0.4)' }}
|
|
233
|
-
aria-hidden
|
|
234
|
-
/>
|
|
235
|
-
{isActive && (
|
|
236
|
-
<span className="absolute right-1.5 top-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-white text-black shadow-sm">
|
|
237
|
-
<Check className="h-2.5 w-2.5" aria-hidden />
|
|
238
|
-
</span>
|
|
239
|
-
)}
|
|
240
|
-
</div>
|
|
241
|
-
<div className="flex items-center justify-between gap-2 px-3 py-2">
|
|
242
|
-
<span className="text-[12px] font-medium capitalize text-[rgb(var(--ec-page-text))]">{preview.label}</span>
|
|
243
|
-
{isCustom && (
|
|
244
|
-
<span className="rounded-full border border-[rgb(var(--ec-page-border))] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
|
|
245
|
-
custom
|
|
246
|
-
</span>
|
|
188
|
+
{!canEdit && (
|
|
189
|
+
<div className="pb-6">
|
|
190
|
+
<ReadOnlyBanner />
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
<Row
|
|
195
|
+
title="Theme"
|
|
196
|
+
description="Pick a built-in colour theme. Selection updates the preview live; click Save to persist."
|
|
197
|
+
canEdit={canEdit}
|
|
198
|
+
dirty={dirtyKeys.has('theme')}
|
|
199
|
+
saving={savingKey === 'theme'}
|
|
200
|
+
onSave={() => saveRow('theme')}
|
|
201
|
+
error={errors.theme}
|
|
202
|
+
>
|
|
203
|
+
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
|
204
|
+
{themeOptions(form.theme).map((t) => {
|
|
205
|
+
const preview = THEME_PREVIEWS[t] ?? { accent: '#888', bg: '#1a1a1a', label: t };
|
|
206
|
+
const isActive = form.theme === t;
|
|
207
|
+
const isCustom = !isKnownTheme(t);
|
|
208
|
+
return (
|
|
209
|
+
<button
|
|
210
|
+
type="button"
|
|
211
|
+
key={t}
|
|
212
|
+
onClick={() => setField('theme', t)}
|
|
213
|
+
disabled={!canEdit}
|
|
214
|
+
aria-pressed={isActive}
|
|
215
|
+
className={cn(
|
|
216
|
+
'group relative flex flex-col overflow-hidden rounded-lg border text-left transition-all',
|
|
217
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
218
|
+
isActive
|
|
219
|
+
? 'border-[rgb(var(--ec-page-text))] ring-2 ring-[rgb(var(--ec-page-text)/0.15)]'
|
|
220
|
+
: 'border-[rgb(var(--ec-page-border))] hover:border-[rgb(var(--ec-page-text-muted)/0.6)]'
|
|
247
221
|
)}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
222
|
+
>
|
|
223
|
+
<div className="relative h-14 w-full overflow-hidden" style={{ background: preview.bg }}>
|
|
224
|
+
<div
|
|
225
|
+
className="absolute -bottom-2 -right-2 h-12 w-12 rounded-full opacity-90 blur-md"
|
|
226
|
+
style={{ background: preview.accent }}
|
|
227
|
+
aria-hidden
|
|
228
|
+
/>
|
|
229
|
+
<div
|
|
230
|
+
className="absolute left-3 top-3 h-1.5 w-10 rounded-full opacity-90"
|
|
231
|
+
style={{ background: preview.accent }}
|
|
232
|
+
aria-hidden
|
|
233
|
+
/>
|
|
234
|
+
<div
|
|
235
|
+
className="absolute left-3 top-6 h-1 w-7 rounded-full opacity-60"
|
|
236
|
+
style={{ background: 'rgba(255,255,255,0.4)' }}
|
|
237
|
+
aria-hidden
|
|
238
|
+
/>
|
|
239
|
+
{isActive && (
|
|
240
|
+
<span className="absolute right-1.5 top-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-white text-black shadow-sm">
|
|
241
|
+
<Check className="h-2.5 w-2.5" aria-hidden />
|
|
242
|
+
</span>
|
|
243
|
+
)}
|
|
244
|
+
</div>
|
|
245
|
+
<div className="flex items-center justify-between gap-2 px-3 py-2">
|
|
246
|
+
<span className="text-[12px] font-medium capitalize text-[rgb(var(--ec-page-text))]">{preview.label}</span>
|
|
247
|
+
{isCustom && (
|
|
248
|
+
<span className="rounded-full border border-[rgb(var(--ec-page-border))] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
|
|
249
|
+
custom
|
|
250
|
+
</span>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
</button>
|
|
254
|
+
);
|
|
255
|
+
})}
|
|
256
|
+
</div>
|
|
257
|
+
</Row>
|
|
254
258
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
259
|
+
<Row
|
|
260
|
+
title="Organization"
|
|
261
|
+
description="Used as the catalog name, the brand line next to the logo, and OpenGraph metadata."
|
|
262
|
+
canEdit={canEdit}
|
|
263
|
+
dirty={dirtyKeys.has('organization')}
|
|
264
|
+
saving={savingKey === 'organization'}
|
|
265
|
+
onSave={() => saveRow('organization')}
|
|
266
|
+
error={errors.organization}
|
|
267
|
+
>
|
|
268
|
+
<input
|
|
269
|
+
type="text"
|
|
270
|
+
value={form.organization}
|
|
271
|
+
onChange={handleChange('organization')}
|
|
272
|
+
disabled={!canEdit}
|
|
273
|
+
maxLength={100}
|
|
274
|
+
placeholder="Acme Inc."
|
|
275
|
+
className={cn(inputBase, errors.organization && inputError)}
|
|
276
|
+
required
|
|
277
|
+
/>
|
|
278
|
+
</Row>
|
|
275
279
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
280
|
+
<Row
|
|
281
|
+
title="Tagline"
|
|
282
|
+
description="A short subtitle shown beneath the organization name on the homepage."
|
|
283
|
+
canEdit={canEdit}
|
|
284
|
+
dirty={dirtyKeys.has('tagline')}
|
|
285
|
+
saving={savingKey === 'tagline'}
|
|
286
|
+
onSave={() => saveRow('tagline')}
|
|
287
|
+
error={errors.tagline}
|
|
288
|
+
>
|
|
289
|
+
<textarea
|
|
290
|
+
value={form.tagline}
|
|
291
|
+
onChange={handleChange('tagline')}
|
|
292
|
+
disabled={!canEdit}
|
|
293
|
+
maxLength={500}
|
|
294
|
+
rows={2}
|
|
295
|
+
placeholder="Discover and explore our events, services, and architecture."
|
|
296
|
+
className={cn(inputBase, 'resize-y', errors.tagline && inputError)}
|
|
297
|
+
/>
|
|
298
|
+
</Row>
|
|
295
299
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
300
|
+
<Row
|
|
301
|
+
title="Homepage link"
|
|
302
|
+
description="Where the logo links to. Useful for pointing back to your main site."
|
|
303
|
+
canEdit={canEdit}
|
|
304
|
+
dirty={dirtyKeys.has('homepageLink')}
|
|
305
|
+
saving={savingKey === 'homepageLink'}
|
|
306
|
+
onSave={() => saveRow('homepageLink')}
|
|
307
|
+
error={errors.homepageLink}
|
|
308
|
+
>
|
|
309
|
+
<input
|
|
310
|
+
type="url"
|
|
311
|
+
value={form.homepageLink}
|
|
312
|
+
onChange={handleChange('homepageLink')}
|
|
313
|
+
onBlur={handleUrlBlur('homepageLink')}
|
|
314
|
+
disabled={!canEdit}
|
|
315
|
+
placeholder="https://example.com"
|
|
316
|
+
className={cn(inputBase, monoInput, errors.homepageLink && inputError)}
|
|
317
|
+
/>
|
|
318
|
+
</Row>
|
|
315
319
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
320
|
+
<Row
|
|
321
|
+
title="Edit URL"
|
|
322
|
+
description="Base URL for the “Edit on GitHub” links shown on resource pages."
|
|
323
|
+
canEdit={canEdit}
|
|
324
|
+
dirty={dirtyKeys.has('editUrl')}
|
|
325
|
+
saving={savingKey === 'editUrl'}
|
|
326
|
+
onSave={() => saveRow('editUrl')}
|
|
327
|
+
error={errors.editUrl}
|
|
328
|
+
>
|
|
329
|
+
<input
|
|
330
|
+
type="url"
|
|
331
|
+
value={form.editUrl}
|
|
332
|
+
onChange={handleChange('editUrl')}
|
|
333
|
+
onBlur={handleUrlBlur('editUrl')}
|
|
334
|
+
disabled={!canEdit}
|
|
335
|
+
placeholder="https://github.com/org/repo/edit/main"
|
|
336
|
+
className={cn(inputBase, monoInput, errors.editUrl && inputError)}
|
|
337
|
+
/>
|
|
338
|
+
</Row>
|
|
335
339
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
340
|
+
<Row
|
|
341
|
+
title="Repository URL"
|
|
342
|
+
description="Link to your catalog's source repository. Shown in the header for quick navigation back to the code."
|
|
343
|
+
canEdit={canEdit}
|
|
344
|
+
dirty={dirtyKeys.has('repositoryUrl')}
|
|
345
|
+
saving={savingKey === 'repositoryUrl'}
|
|
346
|
+
onSave={() => saveRow('repositoryUrl')}
|
|
347
|
+
error={errors.repositoryUrl}
|
|
348
|
+
>
|
|
349
|
+
<input
|
|
350
|
+
type="url"
|
|
351
|
+
value={form.repositoryUrl}
|
|
352
|
+
onChange={handleChange('repositoryUrl')}
|
|
353
|
+
onBlur={handleUrlBlur('repositoryUrl')}
|
|
354
|
+
disabled={!canEdit}
|
|
355
|
+
placeholder="https://github.com/org/repo"
|
|
356
|
+
className={cn(inputBase, monoInput, errors.repositoryUrl && inputError)}
|
|
357
|
+
/>
|
|
358
|
+
</Row>
|
|
355
359
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
canEdit={canEdit}
|
|
360
|
-
// Logo upload saves itself directly; no inline Save button needed.
|
|
361
|
-
dirty={false}
|
|
362
|
-
>
|
|
363
|
-
<LogoUpload
|
|
360
|
+
<Row
|
|
361
|
+
title="Logo"
|
|
362
|
+
description="Image shown in the top-left corner. PNG, JPG, SVG, or WebP up to 2MB."
|
|
364
363
|
canEdit={canEdit}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
364
|
+
// Logo upload saves itself directly; no inline Save button needed.
|
|
365
|
+
dirty={false}
|
|
366
|
+
>
|
|
367
|
+
<LogoUpload
|
|
368
|
+
canEdit={canEdit}
|
|
369
|
+
initialSrc={initial.logo?.src ? `${initial.logo.src}?t=${Date.now()}` : undefined}
|
|
370
|
+
apiUrl={`${apiBase}/logo`}
|
|
371
|
+
/>
|
|
372
|
+
</Row>
|
|
373
|
+
</form>
|
|
374
|
+
</>
|
|
370
375
|
);
|
|
371
376
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import { ArrowUpRight, Clock, TriangleAlert } from 'lucide-react';
|
|
2
|
+
import { ArrowUpRight, Clock, Info, Mail, TriangleAlert } from 'lucide-react';
|
|
3
3
|
import { Row, cn } from './Row';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BUY_BUSINESS_URL,
|
|
6
|
+
getTrialStatus,
|
|
7
|
+
LICENSE_FAQ_URL,
|
|
8
|
+
LICENSE_REMINDER_DAYS,
|
|
9
|
+
LICENSE_RENEW_URL,
|
|
10
|
+
PRICING_URL,
|
|
11
|
+
TALK_TO_US_URL,
|
|
12
|
+
TRIAL_LENGTH_DAYS,
|
|
13
|
+
} from '@utils/trial';
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* The license status (see getLicenseStatus in @utils/license) with dates as unix ms, so it can be
|
|
@@ -35,7 +44,8 @@ const PILL_TONES: Record<Tone, string> = {
|
|
|
35
44
|
'border-[rgb(var(--ec-badge-color-amber-text)/0.3)] bg-[rgb(var(--ec-badge-color-amber-background))] text-[rgb(var(--ec-badge-color-amber-text))]',
|
|
36
45
|
danger:
|
|
37
46
|
'border-[rgb(var(--ec-badge-color-red-text)/0.3)] bg-[rgb(var(--ec-badge-color-red-background))] text-[rgb(var(--ec-badge-color-red-text))]',
|
|
38
|
-
|
|
47
|
+
// Black on light themes, white on dark: the catalog's accent colour reads as a warning
|
|
48
|
+
info: 'border-transparent bg-[rgb(var(--ec-page-text))] text-[rgb(var(--ec-page-bg))]',
|
|
39
49
|
neutral: 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))]',
|
|
40
50
|
};
|
|
41
51
|
|
|
@@ -120,11 +130,21 @@ const LicenseNotice = ({ license, now }: { license: LicenseProps; now: number })
|
|
|
120
130
|
<p className="text-[13px] font-semibold">
|
|
121
131
|
{expired ? 'Your commercial license has expired' : `Your commercial license expires in ${formatDays(daysLeft!)}`}
|
|
122
132
|
</p>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
{expired ? (
|
|
134
|
+
<>
|
|
135
|
+
<p className="text-[13px] opacity-90">
|
|
136
|
+
It expired on {formatDate(expiresAt)}. Organisations that don't qualify for Free Use need a commercial license to
|
|
137
|
+
use EventCatalog in production.
|
|
138
|
+
</p>
|
|
139
|
+
<p className="text-[13px] opacity-90">
|
|
140
|
+
Your catalog keeps building and running. Renew to stay licensed, or talk to us if anything has changed.
|
|
141
|
+
</p>
|
|
142
|
+
</>
|
|
143
|
+
) : (
|
|
144
|
+
<p className="text-[13px] opacity-90">
|
|
145
|
+
It expires on {formatDate(expiresAt)}. Renew it before then to keep using EventCatalog commercially.
|
|
146
|
+
</p>
|
|
147
|
+
)}
|
|
128
148
|
<div className="flex flex-wrap items-center gap-3 pt-2">
|
|
129
149
|
<a
|
|
130
150
|
href={LICENSE_RENEW_URL}
|
|
@@ -135,6 +155,14 @@ const LicenseNotice = ({ license, now }: { license: LicenseProps; now: number })
|
|
|
135
155
|
Renew license
|
|
136
156
|
<ArrowUpRight className="h-3.5 w-3.5" aria-hidden />
|
|
137
157
|
</a>
|
|
158
|
+
<a
|
|
159
|
+
href={TALK_TO_US_URL}
|
|
160
|
+
target="_blank"
|
|
161
|
+
rel="noreferrer"
|
|
162
|
+
className="text-[13px] font-medium underline underline-offset-2 hover:opacity-80"
|
|
163
|
+
>
|
|
164
|
+
Talk to us
|
|
165
|
+
</a>
|
|
138
166
|
<a
|
|
139
167
|
href={LICENSE_FAQ_URL}
|
|
140
168
|
target="_blank"
|
|
@@ -149,6 +177,55 @@ const LicenseNotice = ({ license, now }: { license: LicenseProps; now: number })
|
|
|
149
177
|
);
|
|
150
178
|
};
|
|
151
179
|
|
|
180
|
+
// Where a customer with a legacy key asks for a license.jwt (as the license FAQ does)
|
|
181
|
+
const LICENSE_CONTACT_EMAIL = 'hello@eventcatalog.dev';
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* A Scale or Starter plan uses a license key from before EventCatalog 5, checked online. It still works, but the
|
|
185
|
+
* customer should ask us for a license.jwt (checked offline) for their plan.
|
|
186
|
+
*/
|
|
187
|
+
const LegacyKeyNotice = ({ plan }: { plan: 'scale' | 'starter' }) => {
|
|
188
|
+
const subject = encodeURIComponent(`A license.jwt for our ${PLAN_NAMES[plan]} plan`);
|
|
189
|
+
return (
|
|
190
|
+
<div
|
|
191
|
+
role="note"
|
|
192
|
+
className="flex gap-3 rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-text)/0.05)] p-4 text-[rgb(var(--ec-page-text))]"
|
|
193
|
+
>
|
|
194
|
+
<Info className="mt-0.5 h-4 w-4 shrink-0 text-[rgb(var(--ec-page-text))]" aria-hidden />
|
|
195
|
+
<div className="space-y-1">
|
|
196
|
+
<p className="text-[13px] font-semibold">You're using a legacy license key</p>
|
|
197
|
+
<p className="text-[13px] text-[rgb(var(--ec-page-text-muted))]">
|
|
198
|
+
Your plan still works. Its key is from before EventCatalog 5.
|
|
199
|
+
</p>
|
|
200
|
+
<p className="text-[13px] text-[rgb(var(--ec-page-text-muted))]">
|
|
201
|
+
Contact us for a new key: a license.jwt file, checked offline.
|
|
202
|
+
</p>
|
|
203
|
+
<div className="pt-2">
|
|
204
|
+
<a
|
|
205
|
+
href={`mailto:${LICENSE_CONTACT_EMAIL}?subject=${subject}`}
|
|
206
|
+
className="inline-flex items-center gap-1.5 rounded-md bg-[rgb(var(--ec-page-text))] px-3.5 py-1.5 text-[13px] font-medium text-[rgb(var(--ec-page-bg))] shadow-sm transition-opacity hover:opacity-85"
|
|
207
|
+
>
|
|
208
|
+
Contact us for a new key
|
|
209
|
+
<Mail className="h-3.5 w-3.5" aria-hidden />
|
|
210
|
+
</a>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// For organisations that don't need to pay: without it, the trial reads as "EventCatalog is now paid"
|
|
218
|
+
const FreeUseNotice = () => (
|
|
219
|
+
<p className="text-[13px] leading-relaxed text-[rgb(var(--ec-content-text-muted,var(--ec-page-text-muted)))]">
|
|
220
|
+
<span className="font-medium text-[rgb(var(--ec-page-text))]">You may qualify for Free Use.</span> Under $10M revenue and $10M
|
|
221
|
+
raised, an individual or an open-source project? You can use EventCatalog for free.{' '}
|
|
222
|
+
<a href={LICENSE_RENEW_URL} target="_blank" rel="noreferrer" className="underline underline-offset-2 hover:opacity-80">
|
|
223
|
+
Get a free key at eventcatalog.cloud
|
|
224
|
+
</a>
|
|
225
|
+
.
|
|
226
|
+
</p>
|
|
227
|
+
);
|
|
228
|
+
|
|
152
229
|
const LicenseTable = ({ license, now }: { license: LicenseProps; now: number }) => {
|
|
153
230
|
if (license.state === 'plan') {
|
|
154
231
|
return (
|
|
@@ -243,46 +320,78 @@ interface Props {
|
|
|
243
320
|
trialStartedAt?: number;
|
|
244
321
|
/** When the page was rendered, unix ms: the first render matches the server's, then it counts from now */
|
|
245
322
|
renderedAt: number;
|
|
323
|
+
/** EventCatalog's license (the Business Source License) on GitHub */
|
|
324
|
+
licenseUrl: string;
|
|
246
325
|
}
|
|
247
326
|
|
|
248
|
-
export const LicenseSettings = ({ license, trialStartedAt, renderedAt }: Props) => {
|
|
327
|
+
export const LicenseSettings = ({ license, trialStartedAt, renderedAt, licenseUrl }: Props) => {
|
|
249
328
|
const [now, setNow] = useState(renderedAt);
|
|
250
329
|
useEffect(() => setNow(Date.now()), []);
|
|
330
|
+
// A catalog is on the trial or has a license: an expired license is shown (to renew), not the trial. As in the header.
|
|
331
|
+
const onTrial = license.state === 'none' || license.state === 'invalid';
|
|
332
|
+
// While the trial runs, buying starts from pricing; once it has ended, from the Business order form
|
|
333
|
+
const trialEnded = onTrial && getTrialStatus(trialStartedAt, now)?.ended;
|
|
251
334
|
|
|
252
335
|
return (
|
|
253
|
-
|
|
336
|
+
// A top border, as on the other settings pages
|
|
337
|
+
<div className="divide-y divide-[rgb(var(--ec-page-border))] border-t border-[rgb(var(--ec-page-border))]">
|
|
254
338
|
<Row
|
|
255
339
|
title="License"
|
|
256
|
-
description=
|
|
340
|
+
description={
|
|
341
|
+
<>
|
|
342
|
+
EventCatalog is licensed under the{' '}
|
|
343
|
+
<a href={licenseUrl} target="_blank" rel="noreferrer" className="underline underline-offset-2 hover:opacity-80">
|
|
344
|
+
Business Source License 1.1
|
|
345
|
+
</a>
|
|
346
|
+
. Your license is read from the license.jwt file in the root of your catalog. The{' '}
|
|
347
|
+
<a href={LICENSE_FAQ_URL} target="_blank" rel="noreferrer" className="underline underline-offset-2 hover:opacity-80">
|
|
348
|
+
license FAQ
|
|
349
|
+
</a>{' '}
|
|
350
|
+
explains who can use EventCatalog for free.
|
|
351
|
+
</>
|
|
352
|
+
}
|
|
353
|
+
aside={
|
|
354
|
+
(onTrial || license.state === 'expired') && (
|
|
355
|
+
<div className="space-y-3">
|
|
356
|
+
<FreeUseNotice />
|
|
357
|
+
<p className="text-[13px] leading-relaxed text-[rgb(var(--ec-content-text-muted,var(--ec-page-text-muted)))]">
|
|
358
|
+
Questions about licensing, or need something different? Get in touch and let us know how we can help.
|
|
359
|
+
</p>
|
|
360
|
+
</div>
|
|
361
|
+
)
|
|
362
|
+
}
|
|
257
363
|
canEdit={false}
|
|
258
364
|
dirty={false}
|
|
259
365
|
>
|
|
260
366
|
<div className="space-y-4">
|
|
261
367
|
<LicenseTable license={license} now={now} />
|
|
262
368
|
<LicenseNotice license={license} now={now} />
|
|
263
|
-
{
|
|
264
|
-
{
|
|
265
|
-
|
|
369
|
+
{license.state === 'plan' && <LegacyKeyNotice plan={license.plan} />}
|
|
370
|
+
{onTrial && <TrialTable trialStartedAt={trialStartedAt} now={now} />}
|
|
371
|
+
{onTrial && (
|
|
372
|
+
<div className="flex flex-wrap gap-3">
|
|
373
|
+
<a
|
|
374
|
+
href={trialEnded ? BUY_BUSINESS_URL : PRICING_URL}
|
|
375
|
+
target="_blank"
|
|
376
|
+
rel="noreferrer"
|
|
377
|
+
className="inline-flex items-center gap-1.5 rounded-md bg-[rgb(var(--ec-page-text))] px-4 py-2 text-[13px] font-medium text-[rgb(var(--ec-page-bg))] shadow-sm transition-opacity hover:opacity-85"
|
|
378
|
+
>
|
|
379
|
+
{trialEnded ? 'Buy Business' : 'Buy a license'}
|
|
380
|
+
<ArrowUpRight className="h-3.5 w-3.5" aria-hidden />
|
|
381
|
+
</a>
|
|
382
|
+
<a
|
|
383
|
+
href={TALK_TO_US_URL}
|
|
384
|
+
target="_blank"
|
|
385
|
+
rel="noreferrer"
|
|
386
|
+
className="inline-flex items-center gap-1.5 rounded-md border border-[rgb(var(--ec-page-border))] px-4 py-2 text-[13px] font-medium text-[rgb(var(--ec-page-text))] transition-opacity hover:opacity-80"
|
|
387
|
+
>
|
|
388
|
+
Talk to us
|
|
389
|
+
<ArrowUpRight className="h-3.5 w-3.5" aria-hidden />
|
|
390
|
+
</a>
|
|
391
|
+
</div>
|
|
266
392
|
)}
|
|
267
393
|
</div>
|
|
268
394
|
</Row>
|
|
269
|
-
|
|
270
|
-
<Row
|
|
271
|
-
title="Learn more"
|
|
272
|
-
description="Who can use EventCatalog for free, and when you need a commercial license."
|
|
273
|
-
canEdit={false}
|
|
274
|
-
dirty={false}
|
|
275
|
-
>
|
|
276
|
-
<a
|
|
277
|
-
href={LICENSE_FAQ_URL}
|
|
278
|
-
target="_blank"
|
|
279
|
-
rel="noreferrer"
|
|
280
|
-
className="inline-flex items-center gap-1.5 rounded-md bg-[rgb(var(--ec-page-text))] px-4 py-2 text-[13px] font-medium text-[rgb(var(--ec-page-bg))] shadow-sm transition-opacity hover:opacity-85"
|
|
281
|
-
>
|
|
282
|
-
Read the license FAQ
|
|
283
|
-
<ArrowUpRight className="h-3.5 w-3.5" aria-hidden />
|
|
284
|
-
</a>
|
|
285
|
-
</Row>
|
|
286
395
|
</div>
|
|
287
396
|
);
|
|
288
397
|
};
|