camox 0.48.0 → 0.51.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/dist/core/layoutContext.d.ts +1 -0
- package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
- package/dist/features/content/ContentAssets.js +84 -88
- package/dist/features/preview/CamoxPreview.js +1 -1
- package/dist/features/preview/EditablePageExperience.js +17 -9
- package/dist/features/preview/components/BlockActionsPopover.js +11 -35
- package/dist/features/preview/components/FieldToolbar.js +1 -1
- package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
- package/dist/features/preview/components/PreviewPanel.js +10 -64
- package/dist/features/preview/components/PreviewToolbar.js +1 -1
- package/dist/features/preview/previewConstants.js +2 -8
- package/dist/features/preview/usePreviewViewportActions.js +1 -1
- package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
- package/dist/features/provider/components/CommandPalette.js +2 -5
- package/dist/features/provider/signInShortcut.js +1 -2
- package/dist/features/provider/useAdminShortcuts.js +47 -53
- package/dist/features/routes/pageRuntime.d.ts +2 -1
- package/dist/features/routes/pageRuntime.js +27 -20
- package/dist/features/runtime/publicCache.js +83 -0
- package/dist/features/runtime/runtime.d.ts +13 -3
- package/dist/features/runtime/runtime.js +173 -79
- package/dist/features/runtime/worker.d.ts +5 -1
- package/dist/features/runtime/worker.js +20 -3
- package/dist/features/settings/AnalyticsSettings.js +622 -0
- package/dist/features/settings/CamoxSettings.js +95 -0
- package/dist/features/settings/FaviconSettings.js +254 -0
- package/dist/features/settings/ProjectNameSettings.js +145 -0
- package/dist/features/settings/SettingsPart.js +73 -0
- package/dist/features/settings/SocialProfilesSettings.js +245 -0
- package/dist/features/settings/useProjectMutation.js +61 -0
- package/dist/features/studio/components/DraftsControl.js +136 -131
- package/dist/features/studio/components/LocalePicker.js +178 -174
- package/dist/features/studio/components/Navbar.js +107 -66
- package/dist/features/studio/components/ProjectMenu.js +118 -170
- package/dist/features/studio/components/ReviewDraftSheet.js +1046 -990
- package/dist/features/studio/components/UserButton.js +1 -1
- package/dist/features/studio/routes.js +3 -1
- package/dist/features/studio/showInCanvas.js +129 -0
- package/dist/features/vite/definitionsSync.js +4 -5
- package/dist/features/vite/runtimeDev.js +32 -17
- package/dist/features/vite/staticAssetHeaders.js +52 -0
- package/dist/features/vite/vite.d.ts +1 -1
- package/dist/features/vite/vite.js +16 -4
- package/dist/lib/api-client-server.d.ts +1 -1
- package/dist/lib/api-client.d.ts +2 -1
- package/dist/lib/draft-scope.d.ts +1 -0
- package/dist/lib/locale-path.d.ts +3 -11
- package/dist/lib/locale-path.js +3 -27
- package/dist/lib/queries.js +7 -1
- package/dist/lib/shortcuts.js +29 -0
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/utils.js +1 -104
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +5 -4
- package/dist/features/studio/components/FaviconDialog.js +0 -318
- package/dist/features/studio/components/SocialProfilesDialog.js +0 -342
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
import { SettingsPart } from "./SettingsPart.js";
|
|
2
|
+
import { useProjectMutation } from "./useProjectMutation.js";
|
|
3
|
+
import { c } from "react/compiler-runtime";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Button } from "@camox/ui/button";
|
|
7
|
+
import { Input } from "@camox/ui/input";
|
|
8
|
+
import { Label } from "@camox/ui/label";
|
|
9
|
+
import { useForm, useStore } from "@tanstack/react-form";
|
|
10
|
+
import { Plus, Trash2 } from "lucide-react";
|
|
11
|
+
import { Spinner } from "@camox/ui/spinner";
|
|
12
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
|
|
13
|
+
import { Separator } from "@camox/ui/separator";
|
|
14
|
+
import { ga4MeasurementIdSchema, posthogProjectApiKeySchema, posthogRegionSchema } from "@camox/api-contract/analytics";
|
|
15
|
+
|
|
16
|
+
//#region src/features/settings/AnalyticsSettings.tsx
|
|
17
|
+
/** Saves a provider's settings, or removes the provider when given null. */
|
|
18
|
+
function useSaveProvider(provider, name, t0) {
|
|
19
|
+
const $ = c(21);
|
|
20
|
+
const { project, onClose } = t0;
|
|
21
|
+
let t1;
|
|
22
|
+
if ($[0] !== name || $[1] !== provider) {
|
|
23
|
+
t1 = (input) => input[provider] === null ? `${name} removed` : `${name} saved`;
|
|
24
|
+
$[0] = name;
|
|
25
|
+
$[1] = provider;
|
|
26
|
+
$[2] = t1;
|
|
27
|
+
} else t1 = $[2];
|
|
28
|
+
const t2 = `Could not save ${name}`;
|
|
29
|
+
let t3;
|
|
30
|
+
if ($[3] !== onClose || $[4] !== provider) {
|
|
31
|
+
t3 = (input_0) => {
|
|
32
|
+
if (input_0[provider] === null) onClose();
|
|
33
|
+
};
|
|
34
|
+
$[3] = onClose;
|
|
35
|
+
$[4] = provider;
|
|
36
|
+
$[5] = t3;
|
|
37
|
+
} else t3 = $[5];
|
|
38
|
+
let t4;
|
|
39
|
+
if ($[6] !== t1 || $[7] !== t2 || $[8] !== t3) {
|
|
40
|
+
t4 = {
|
|
41
|
+
success: t1,
|
|
42
|
+
failure: t2,
|
|
43
|
+
onSuccess: t3
|
|
44
|
+
};
|
|
45
|
+
$[6] = t1;
|
|
46
|
+
$[7] = t2;
|
|
47
|
+
$[8] = t3;
|
|
48
|
+
$[9] = t4;
|
|
49
|
+
} else t4 = $[9];
|
|
50
|
+
const mutation = useProjectMutation(project, "updateAnalytics", t4);
|
|
51
|
+
let t5;
|
|
52
|
+
if ($[10] !== project || $[11] !== provider) {
|
|
53
|
+
t5 = (settings) => ({
|
|
54
|
+
id: project.id,
|
|
55
|
+
[provider]: settings
|
|
56
|
+
});
|
|
57
|
+
$[10] = project;
|
|
58
|
+
$[11] = provider;
|
|
59
|
+
$[12] = t5;
|
|
60
|
+
} else t5 = $[12];
|
|
61
|
+
const input_1 = t5;
|
|
62
|
+
let t6;
|
|
63
|
+
let t7;
|
|
64
|
+
if ($[13] !== input_1 || $[14] !== mutation) {
|
|
65
|
+
t6 = (settings_0) => mutation.mutate(input_1(settings_0));
|
|
66
|
+
t7 = (settings_1) => mutation.mutateAsync(input_1(settings_1));
|
|
67
|
+
$[13] = input_1;
|
|
68
|
+
$[14] = mutation;
|
|
69
|
+
$[15] = t6;
|
|
70
|
+
$[16] = t7;
|
|
71
|
+
} else {
|
|
72
|
+
t6 = $[15];
|
|
73
|
+
t7 = $[16];
|
|
74
|
+
}
|
|
75
|
+
let t8;
|
|
76
|
+
if ($[17] !== mutation.isPending || $[18] !== t6 || $[19] !== t7) {
|
|
77
|
+
t8 = {
|
|
78
|
+
isPending: mutation.isPending,
|
|
79
|
+
mutate: t6,
|
|
80
|
+
mutateAsync: t7
|
|
81
|
+
};
|
|
82
|
+
$[17] = mutation.isPending;
|
|
83
|
+
$[18] = t6;
|
|
84
|
+
$[19] = t7;
|
|
85
|
+
$[20] = t8;
|
|
86
|
+
} else t8 = $[20];
|
|
87
|
+
return t8;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A provider's form: its fields, then Save, and either Cancel while adding it or Remove once it's
|
|
91
|
+
* saved.
|
|
92
|
+
*/
|
|
93
|
+
function ProviderForm(t0) {
|
|
94
|
+
const $ = c(20);
|
|
95
|
+
const { name, saved, dirty, submitting, save, onSubmit, onCancel, children } = t0;
|
|
96
|
+
let t1;
|
|
97
|
+
if ($[0] !== onSubmit) {
|
|
98
|
+
t1 = (event) => {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
onSubmit();
|
|
102
|
+
};
|
|
103
|
+
$[0] = onSubmit;
|
|
104
|
+
$[1] = t1;
|
|
105
|
+
} else t1 = $[1];
|
|
106
|
+
const t2 = !dirty || submitting || save.isPending;
|
|
107
|
+
let t3;
|
|
108
|
+
if ($[2] !== submitting) {
|
|
109
|
+
t3 = submitting && /* @__PURE__ */ jsx(Spinner, {});
|
|
110
|
+
$[2] = submitting;
|
|
111
|
+
$[3] = t3;
|
|
112
|
+
} else t3 = $[3];
|
|
113
|
+
let t4;
|
|
114
|
+
if ($[4] !== t2 || $[5] !== t3) {
|
|
115
|
+
t4 = /* @__PURE__ */ jsxs(Button, {
|
|
116
|
+
type: "submit",
|
|
117
|
+
disabled: t2,
|
|
118
|
+
children: [t3, "Save"]
|
|
119
|
+
});
|
|
120
|
+
$[4] = t2;
|
|
121
|
+
$[5] = t3;
|
|
122
|
+
$[6] = t4;
|
|
123
|
+
} else t4 = $[6];
|
|
124
|
+
let t5;
|
|
125
|
+
if ($[7] !== name || $[8] !== onCancel || $[9] !== save || $[10] !== saved || $[11] !== submitting) {
|
|
126
|
+
t5 = saved ? /* @__PURE__ */ jsxs(Button, {
|
|
127
|
+
type: "button",
|
|
128
|
+
variant: "ghost",
|
|
129
|
+
className: "ml-auto",
|
|
130
|
+
disabled: save.isPending,
|
|
131
|
+
onClick: () => save.mutate(null),
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ jsx(Trash2, {}),
|
|
134
|
+
"Remove ",
|
|
135
|
+
name
|
|
136
|
+
]
|
|
137
|
+
}) : /* @__PURE__ */ jsx(Button, {
|
|
138
|
+
type: "button",
|
|
139
|
+
variant: "outline",
|
|
140
|
+
disabled: submitting,
|
|
141
|
+
onClick: onCancel,
|
|
142
|
+
children: "Cancel"
|
|
143
|
+
});
|
|
144
|
+
$[7] = name;
|
|
145
|
+
$[8] = onCancel;
|
|
146
|
+
$[9] = save;
|
|
147
|
+
$[10] = saved;
|
|
148
|
+
$[11] = submitting;
|
|
149
|
+
$[12] = t5;
|
|
150
|
+
} else t5 = $[12];
|
|
151
|
+
let t6;
|
|
152
|
+
if ($[13] !== t4 || $[14] !== t5) {
|
|
153
|
+
t6 = /* @__PURE__ */ jsxs("div", {
|
|
154
|
+
className: "flex flex-wrap gap-2",
|
|
155
|
+
children: [t4, t5]
|
|
156
|
+
});
|
|
157
|
+
$[13] = t4;
|
|
158
|
+
$[14] = t5;
|
|
159
|
+
$[15] = t6;
|
|
160
|
+
} else t6 = $[15];
|
|
161
|
+
let t7;
|
|
162
|
+
if ($[16] !== children || $[17] !== t1 || $[18] !== t6) {
|
|
163
|
+
t7 = /* @__PURE__ */ jsxs("form", {
|
|
164
|
+
onSubmit: t1,
|
|
165
|
+
className: "flex flex-col gap-4",
|
|
166
|
+
children: [children, t6]
|
|
167
|
+
});
|
|
168
|
+
$[16] = children;
|
|
169
|
+
$[17] = t1;
|
|
170
|
+
$[18] = t6;
|
|
171
|
+
$[19] = t7;
|
|
172
|
+
} else t7 = $[19];
|
|
173
|
+
return t7;
|
|
174
|
+
}
|
|
175
|
+
/** A monospace text input for an ID or key, with its validation error. */
|
|
176
|
+
function IdInput(t0) {
|
|
177
|
+
const $ = c(14);
|
|
178
|
+
const { id, placeholder, field } = t0;
|
|
179
|
+
let t1;
|
|
180
|
+
if ($[0] !== field) {
|
|
181
|
+
t1 = (event) => field.handleChange(event.target.value);
|
|
182
|
+
$[0] = field;
|
|
183
|
+
$[1] = t1;
|
|
184
|
+
} else t1 = $[1];
|
|
185
|
+
const t2 = field.state.meta.errors.length ? true : void 0;
|
|
186
|
+
let t3;
|
|
187
|
+
if ($[2] !== field.state.value || $[3] !== id || $[4] !== placeholder || $[5] !== t1 || $[6] !== t2) {
|
|
188
|
+
t3 = /* @__PURE__ */ jsx(Input, {
|
|
189
|
+
id,
|
|
190
|
+
className: "font-mono",
|
|
191
|
+
placeholder,
|
|
192
|
+
autoComplete: "off",
|
|
193
|
+
spellCheck: false,
|
|
194
|
+
value: field.state.value,
|
|
195
|
+
onChange: t1,
|
|
196
|
+
"aria-invalid": t2
|
|
197
|
+
});
|
|
198
|
+
$[2] = field.state.value;
|
|
199
|
+
$[3] = id;
|
|
200
|
+
$[4] = placeholder;
|
|
201
|
+
$[5] = t1;
|
|
202
|
+
$[6] = t2;
|
|
203
|
+
$[7] = t3;
|
|
204
|
+
} else t3 = $[7];
|
|
205
|
+
let t4;
|
|
206
|
+
if ($[8] !== field.state.meta.errors[0] || $[9] !== field.state.meta.errors.length) {
|
|
207
|
+
t4 = field.state.meta.errors.length > 0 && /* @__PURE__ */ jsx("p", {
|
|
208
|
+
className: "text-destructive text-xs",
|
|
209
|
+
children: field.state.meta.errors[0]
|
|
210
|
+
});
|
|
211
|
+
$[8] = field.state.meta.errors[0];
|
|
212
|
+
$[9] = field.state.meta.errors.length;
|
|
213
|
+
$[10] = t4;
|
|
214
|
+
} else t4 = $[10];
|
|
215
|
+
let t5;
|
|
216
|
+
if ($[11] !== t3 || $[12] !== t4) {
|
|
217
|
+
t5 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, t4] });
|
|
218
|
+
$[11] = t3;
|
|
219
|
+
$[12] = t4;
|
|
220
|
+
$[13] = t5;
|
|
221
|
+
} else t5 = $[13];
|
|
222
|
+
return t5;
|
|
223
|
+
}
|
|
224
|
+
function Ga4Form(props) {
|
|
225
|
+
const $ = c(20);
|
|
226
|
+
const { saved, onClose } = props;
|
|
227
|
+
const save = useSaveProvider("ga4", "Google Analytics", props);
|
|
228
|
+
const t0 = saved?.measurementId ?? "";
|
|
229
|
+
let t1;
|
|
230
|
+
if ($[0] !== t0) {
|
|
231
|
+
t1 = { measurementId: t0 };
|
|
232
|
+
$[0] = t0;
|
|
233
|
+
$[1] = t1;
|
|
234
|
+
} else t1 = $[1];
|
|
235
|
+
let t2;
|
|
236
|
+
if ($[2] !== save) {
|
|
237
|
+
t2 = async (t3) => {
|
|
238
|
+
const { value } = t3;
|
|
239
|
+
await save.mutateAsync({ measurementId: value.measurementId.trim().toUpperCase() }).catch(_temp);
|
|
240
|
+
};
|
|
241
|
+
$[2] = save;
|
|
242
|
+
$[3] = t2;
|
|
243
|
+
} else t2 = $[3];
|
|
244
|
+
let t3;
|
|
245
|
+
if ($[4] !== t1 || $[5] !== t2) {
|
|
246
|
+
t3 = {
|
|
247
|
+
defaultValues: t1,
|
|
248
|
+
onSubmit: t2
|
|
249
|
+
};
|
|
250
|
+
$[4] = t1;
|
|
251
|
+
$[5] = t2;
|
|
252
|
+
$[6] = t3;
|
|
253
|
+
} else t3 = $[6];
|
|
254
|
+
const form = useForm(t3);
|
|
255
|
+
const dirty = useStore(form.store, _temp2);
|
|
256
|
+
const submitting = useStore(form.store, _temp3);
|
|
257
|
+
const t4 = !!saved;
|
|
258
|
+
let t5;
|
|
259
|
+
if ($[7] !== form) {
|
|
260
|
+
t5 = () => void form.handleSubmit();
|
|
261
|
+
$[7] = form;
|
|
262
|
+
$[8] = t5;
|
|
263
|
+
} else t5 = $[8];
|
|
264
|
+
let t6;
|
|
265
|
+
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
|
266
|
+
t6 = { onSubmit: _temp4 };
|
|
267
|
+
$[9] = t6;
|
|
268
|
+
} else t6 = $[9];
|
|
269
|
+
let t7;
|
|
270
|
+
if ($[10] !== form.Field) {
|
|
271
|
+
t7 = /* @__PURE__ */ jsx(form.Field, {
|
|
272
|
+
name: "measurementId",
|
|
273
|
+
validators: t6,
|
|
274
|
+
children: _temp5
|
|
275
|
+
});
|
|
276
|
+
$[10] = form.Field;
|
|
277
|
+
$[11] = t7;
|
|
278
|
+
} else t7 = $[11];
|
|
279
|
+
let t8;
|
|
280
|
+
if ($[12] !== dirty || $[13] !== onClose || $[14] !== save || $[15] !== submitting || $[16] !== t4 || $[17] !== t5 || $[18] !== t7) {
|
|
281
|
+
t8 = /* @__PURE__ */ jsx(ProviderForm, {
|
|
282
|
+
name: "Google Analytics",
|
|
283
|
+
saved: t4,
|
|
284
|
+
dirty,
|
|
285
|
+
submitting,
|
|
286
|
+
save,
|
|
287
|
+
onSubmit: t5,
|
|
288
|
+
onCancel: onClose,
|
|
289
|
+
children: t7
|
|
290
|
+
});
|
|
291
|
+
$[12] = dirty;
|
|
292
|
+
$[13] = onClose;
|
|
293
|
+
$[14] = save;
|
|
294
|
+
$[15] = submitting;
|
|
295
|
+
$[16] = t4;
|
|
296
|
+
$[17] = t5;
|
|
297
|
+
$[18] = t7;
|
|
298
|
+
$[19] = t8;
|
|
299
|
+
} else t8 = $[19];
|
|
300
|
+
return t8;
|
|
301
|
+
}
|
|
302
|
+
function _temp5(field) {
|
|
303
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
304
|
+
className: "flex flex-col gap-2",
|
|
305
|
+
children: [
|
|
306
|
+
/* @__PURE__ */ jsx(Label, {
|
|
307
|
+
htmlFor: "camox-ga4-measurement-id",
|
|
308
|
+
children: "Google Analytics 4 measurement ID"
|
|
309
|
+
}),
|
|
310
|
+
/* @__PURE__ */ jsx("p", {
|
|
311
|
+
className: "text-muted-foreground text-sm",
|
|
312
|
+
children: "In Google Analytics, go to Admin, then Data streams, and open your website's stream. The measurement ID is at the top right, starting with G-."
|
|
313
|
+
}),
|
|
314
|
+
/* @__PURE__ */ jsx(IdInput, {
|
|
315
|
+
id: "camox-ga4-measurement-id",
|
|
316
|
+
placeholder: "G-XXXXXXXXXX",
|
|
317
|
+
field
|
|
318
|
+
})
|
|
319
|
+
]
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function _temp4(t0) {
|
|
323
|
+
const { value: value_0 } = t0;
|
|
324
|
+
return ga4MeasurementIdSchema.safeParse(value_0.trim().toUpperCase()).success ? void 0 : "Enter a measurement ID that starts with G-, like G-A1B2C3D4E5";
|
|
325
|
+
}
|
|
326
|
+
function _temp3(state_0) {
|
|
327
|
+
return state_0.isSubmitting;
|
|
328
|
+
}
|
|
329
|
+
function _temp2(state) {
|
|
330
|
+
return state.isDirty;
|
|
331
|
+
}
|
|
332
|
+
function _temp() {}
|
|
333
|
+
const POSTHOG_REGION_LABELS = {
|
|
334
|
+
us: "US Cloud",
|
|
335
|
+
eu: "EU Cloud"
|
|
336
|
+
};
|
|
337
|
+
const POSTHOG_REGIONS = posthogRegionSchema.options.map((value) => ({
|
|
338
|
+
value,
|
|
339
|
+
label: POSTHOG_REGION_LABELS[value]
|
|
340
|
+
}));
|
|
341
|
+
function PosthogForm(props) {
|
|
342
|
+
const $ = c(22);
|
|
343
|
+
const { saved, onClose } = props;
|
|
344
|
+
const save = useSaveProvider("posthog", "PostHog", props);
|
|
345
|
+
const t0 = saved?.projectApiKey ?? "";
|
|
346
|
+
const t1 = saved?.region ?? "us";
|
|
347
|
+
let t2;
|
|
348
|
+
if ($[0] !== t0 || $[1] !== t1) {
|
|
349
|
+
t2 = {
|
|
350
|
+
projectApiKey: t0,
|
|
351
|
+
region: t1
|
|
352
|
+
};
|
|
353
|
+
$[0] = t0;
|
|
354
|
+
$[1] = t1;
|
|
355
|
+
$[2] = t2;
|
|
356
|
+
} else t2 = $[2];
|
|
357
|
+
let t3;
|
|
358
|
+
if ($[3] !== save) {
|
|
359
|
+
t3 = async (t4) => {
|
|
360
|
+
const { value } = t4;
|
|
361
|
+
await save.mutateAsync({
|
|
362
|
+
projectApiKey: value.projectApiKey.trim(),
|
|
363
|
+
region: value.region
|
|
364
|
+
}).catch(_temp6);
|
|
365
|
+
};
|
|
366
|
+
$[3] = save;
|
|
367
|
+
$[4] = t3;
|
|
368
|
+
} else t3 = $[4];
|
|
369
|
+
let t4;
|
|
370
|
+
if ($[5] !== t2 || $[6] !== t3) {
|
|
371
|
+
t4 = {
|
|
372
|
+
defaultValues: t2,
|
|
373
|
+
onSubmit: t3
|
|
374
|
+
};
|
|
375
|
+
$[5] = t2;
|
|
376
|
+
$[6] = t3;
|
|
377
|
+
$[7] = t4;
|
|
378
|
+
} else t4 = $[7];
|
|
379
|
+
const form = useForm(t4);
|
|
380
|
+
const dirty = useStore(form.store, _temp7);
|
|
381
|
+
const submitting = useStore(form.store, _temp8);
|
|
382
|
+
const t5 = !!saved;
|
|
383
|
+
let t6;
|
|
384
|
+
if ($[8] !== form) {
|
|
385
|
+
t6 = () => void form.handleSubmit();
|
|
386
|
+
$[8] = form;
|
|
387
|
+
$[9] = t6;
|
|
388
|
+
} else t6 = $[9];
|
|
389
|
+
let t7;
|
|
390
|
+
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
|
|
391
|
+
t7 = /* @__PURE__ */ jsx("p", {
|
|
392
|
+
className: "text-muted-foreground text-sm",
|
|
393
|
+
children: "In PostHog, go to Project settings. The project API key starts with phc_, and the region is the cloud your project is hosted in, shown in your PostHog address (us.posthog.com or eu.posthog.com)."
|
|
394
|
+
});
|
|
395
|
+
$[10] = t7;
|
|
396
|
+
} else t7 = $[10];
|
|
397
|
+
let t8;
|
|
398
|
+
if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
|
|
399
|
+
t8 = { onSubmit: _temp9 };
|
|
400
|
+
$[11] = t8;
|
|
401
|
+
} else t8 = $[11];
|
|
402
|
+
let t9;
|
|
403
|
+
if ($[12] !== form.Field) {
|
|
404
|
+
t9 = /* @__PURE__ */ jsxs("div", {
|
|
405
|
+
className: "flex flex-wrap items-start gap-4",
|
|
406
|
+
children: [/* @__PURE__ */ jsx(form.Field, {
|
|
407
|
+
name: "projectApiKey",
|
|
408
|
+
validators: t8,
|
|
409
|
+
children: _temp10
|
|
410
|
+
}), /* @__PURE__ */ jsx(form.Field, {
|
|
411
|
+
name: "region",
|
|
412
|
+
children: _temp12
|
|
413
|
+
})]
|
|
414
|
+
});
|
|
415
|
+
$[12] = form.Field;
|
|
416
|
+
$[13] = t9;
|
|
417
|
+
} else t9 = $[13];
|
|
418
|
+
let t10;
|
|
419
|
+
if ($[14] !== dirty || $[15] !== onClose || $[16] !== save || $[17] !== submitting || $[18] !== t5 || $[19] !== t6 || $[20] !== t9) {
|
|
420
|
+
t10 = /* @__PURE__ */ jsxs(ProviderForm, {
|
|
421
|
+
name: "PostHog",
|
|
422
|
+
saved: t5,
|
|
423
|
+
dirty,
|
|
424
|
+
submitting,
|
|
425
|
+
save,
|
|
426
|
+
onSubmit: t6,
|
|
427
|
+
onCancel: onClose,
|
|
428
|
+
children: [t7, t9]
|
|
429
|
+
});
|
|
430
|
+
$[14] = dirty;
|
|
431
|
+
$[15] = onClose;
|
|
432
|
+
$[16] = save;
|
|
433
|
+
$[17] = submitting;
|
|
434
|
+
$[18] = t5;
|
|
435
|
+
$[19] = t6;
|
|
436
|
+
$[20] = t9;
|
|
437
|
+
$[21] = t10;
|
|
438
|
+
} else t10 = $[21];
|
|
439
|
+
return t10;
|
|
440
|
+
}
|
|
441
|
+
function _temp12(field_0) {
|
|
442
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
443
|
+
className: "flex flex-col gap-2",
|
|
444
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
445
|
+
htmlFor: "camox-posthog-region",
|
|
446
|
+
children: "Region"
|
|
447
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
448
|
+
value: field_0.state.value,
|
|
449
|
+
onValueChange: (value_1) => {
|
|
450
|
+
if (value_1 != null) field_0.handleChange(value_1);
|
|
451
|
+
},
|
|
452
|
+
items: POSTHOG_REGIONS,
|
|
453
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
454
|
+
id: "camox-posthog-region",
|
|
455
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
456
|
+
}), /* @__PURE__ */ jsx(SelectContent, { children: POSTHOG_REGIONS.map(_temp11) })]
|
|
457
|
+
})]
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
function _temp11(region) {
|
|
461
|
+
return /* @__PURE__ */ jsx(SelectItem, {
|
|
462
|
+
value: region.value,
|
|
463
|
+
children: region.label
|
|
464
|
+
}, region.value);
|
|
465
|
+
}
|
|
466
|
+
function _temp10(field) {
|
|
467
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
468
|
+
className: "flex min-w-64 flex-1 flex-col gap-2",
|
|
469
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
470
|
+
htmlFor: "camox-posthog-project-api-key",
|
|
471
|
+
children: "PostHog project API key"
|
|
472
|
+
}), /* @__PURE__ */ jsx(IdInput, {
|
|
473
|
+
id: "camox-posthog-project-api-key",
|
|
474
|
+
placeholder: "phc_XXXXXXXXXX",
|
|
475
|
+
field
|
|
476
|
+
})]
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
function _temp9(t0) {
|
|
480
|
+
const { value: value_0 } = t0;
|
|
481
|
+
return posthogProjectApiKeySchema.safeParse(value_0.trim()).success ? void 0 : "Enter a project API key that starts with phc_";
|
|
482
|
+
}
|
|
483
|
+
function _temp8(state_0) {
|
|
484
|
+
return state_0.isSubmitting;
|
|
485
|
+
}
|
|
486
|
+
function _temp7(state) {
|
|
487
|
+
return state.isDirty;
|
|
488
|
+
}
|
|
489
|
+
function _temp6() {}
|
|
490
|
+
/** Each provider the Analytics part offers, in the order it lists them. */
|
|
491
|
+
const PROVIDERS = {
|
|
492
|
+
ga4: {
|
|
493
|
+
addLabel: "Add Google Analytics 4",
|
|
494
|
+
Form: Ga4Form
|
|
495
|
+
},
|
|
496
|
+
posthog: {
|
|
497
|
+
addLabel: "Add PostHog",
|
|
498
|
+
Form: PosthogForm
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
function ProviderSettings(t0) {
|
|
502
|
+
const $ = c(6);
|
|
503
|
+
let props;
|
|
504
|
+
let provider;
|
|
505
|
+
if ($[0] !== t0) {
|
|
506
|
+
({provider, ...props} = t0);
|
|
507
|
+
$[0] = t0;
|
|
508
|
+
$[1] = props;
|
|
509
|
+
$[2] = provider;
|
|
510
|
+
} else {
|
|
511
|
+
props = $[1];
|
|
512
|
+
provider = $[2];
|
|
513
|
+
}
|
|
514
|
+
const { Form } = PROVIDERS[provider];
|
|
515
|
+
let t1;
|
|
516
|
+
if ($[3] !== Form || $[4] !== props) {
|
|
517
|
+
t1 = /* @__PURE__ */ jsx(Form, { ...props });
|
|
518
|
+
$[3] = Form;
|
|
519
|
+
$[4] = props;
|
|
520
|
+
$[5] = t1;
|
|
521
|
+
} else t1 = $[5];
|
|
522
|
+
return t1;
|
|
523
|
+
}
|
|
524
|
+
function AnalyticsSettings(t0) {
|
|
525
|
+
const $ = c(21);
|
|
526
|
+
const { project } = t0;
|
|
527
|
+
const { analytics } = project;
|
|
528
|
+
let t1;
|
|
529
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
530
|
+
t1 = [];
|
|
531
|
+
$[0] = t1;
|
|
532
|
+
} else t1 = $[0];
|
|
533
|
+
const [adding, setAdding] = React.useState(t1);
|
|
534
|
+
let providers;
|
|
535
|
+
let t2;
|
|
536
|
+
if ($[1] !== adding || $[2] !== analytics) {
|
|
537
|
+
providers = Object.keys(PROVIDERS);
|
|
538
|
+
t2 = providers.filter((provider) => analytics[provider] || adding.includes(provider));
|
|
539
|
+
$[1] = adding;
|
|
540
|
+
$[2] = analytics;
|
|
541
|
+
$[3] = providers;
|
|
542
|
+
$[4] = t2;
|
|
543
|
+
} else {
|
|
544
|
+
providers = $[3];
|
|
545
|
+
t2 = $[4];
|
|
546
|
+
}
|
|
547
|
+
const shown = t2;
|
|
548
|
+
let t3;
|
|
549
|
+
if ($[5] !== providers || $[6] !== shown) {
|
|
550
|
+
t3 = providers.filter((provider_0) => !shown.includes(provider_0));
|
|
551
|
+
$[5] = providers;
|
|
552
|
+
$[6] = shown;
|
|
553
|
+
$[7] = t3;
|
|
554
|
+
} else t3 = $[7];
|
|
555
|
+
const addable = t3;
|
|
556
|
+
let t4;
|
|
557
|
+
if ($[8] !== addable) {
|
|
558
|
+
t4 = /* @__PURE__ */ jsx("div", {
|
|
559
|
+
className: "flex flex-wrap justify-center gap-2",
|
|
560
|
+
children: addable.map((provider_1) => /* @__PURE__ */ jsxs(Button, {
|
|
561
|
+
type: "button",
|
|
562
|
+
variant: "outline",
|
|
563
|
+
onClick: () => setAdding((current) => [...current, provider_1]),
|
|
564
|
+
children: [/* @__PURE__ */ jsx(Plus, {}), PROVIDERS[provider_1].addLabel]
|
|
565
|
+
}, provider_1))
|
|
566
|
+
});
|
|
567
|
+
$[8] = addable;
|
|
568
|
+
$[9] = t4;
|
|
569
|
+
} else t4 = $[9];
|
|
570
|
+
const addButtons = t4;
|
|
571
|
+
let t5;
|
|
572
|
+
if ($[10] !== analytics || $[11] !== project || $[12] !== shown) {
|
|
573
|
+
t5 = shown.map((provider_2, index) => {
|
|
574
|
+
const saved = analytics[provider_2] ?? void 0;
|
|
575
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [index > 0 && /* @__PURE__ */ jsx(Separator, {}), /* @__PURE__ */ jsx(ProviderSettings, {
|
|
576
|
+
provider: provider_2,
|
|
577
|
+
project,
|
|
578
|
+
saved,
|
|
579
|
+
onClose: () => setAdding((current_0) => current_0.filter((p) => p !== provider_2))
|
|
580
|
+
}, saved ? JSON.stringify(saved) : "new")] }, provider_2);
|
|
581
|
+
});
|
|
582
|
+
$[10] = analytics;
|
|
583
|
+
$[11] = project;
|
|
584
|
+
$[12] = shown;
|
|
585
|
+
$[13] = t5;
|
|
586
|
+
} else t5 = $[13];
|
|
587
|
+
let t6;
|
|
588
|
+
if ($[14] !== addButtons || $[15] !== addable.length || $[16] !== shown.length) {
|
|
589
|
+
t6 = shown.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
590
|
+
className: "flex flex-col gap-3 rounded-lg border border-dashed p-6 text-center",
|
|
591
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
592
|
+
className: "text-muted-foreground text-sm",
|
|
593
|
+
children: "No analytics yet. Add a provider to count your website's visitors."
|
|
594
|
+
}), addButtons]
|
|
595
|
+
}) : addable.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
596
|
+
className: "flex",
|
|
597
|
+
children: addButtons
|
|
598
|
+
});
|
|
599
|
+
$[14] = addButtons;
|
|
600
|
+
$[15] = addable.length;
|
|
601
|
+
$[16] = shown.length;
|
|
602
|
+
$[17] = t6;
|
|
603
|
+
} else t6 = $[17];
|
|
604
|
+
let t7;
|
|
605
|
+
if ($[18] !== t5 || $[19] !== t6) {
|
|
606
|
+
t7 = /* @__PURE__ */ jsx(SettingsPart, {
|
|
607
|
+
title: "Analytics",
|
|
608
|
+
description: "Count your website's unauthenticated visitors.",
|
|
609
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
610
|
+
className: "flex flex-col gap-4",
|
|
611
|
+
children: [t5, t6]
|
|
612
|
+
})
|
|
613
|
+
});
|
|
614
|
+
$[18] = t5;
|
|
615
|
+
$[19] = t6;
|
|
616
|
+
$[20] = t7;
|
|
617
|
+
} else t7 = $[20];
|
|
618
|
+
return t7;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
//#endregion
|
|
622
|
+
export { AnalyticsSettings };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useProjectUuid } from "../../lib/auth.js";
|
|
2
|
+
import { reads, useReadQuery } from "../../lib/studio-reads.js";
|
|
3
|
+
import { AnalyticsSettings } from "./AnalyticsSettings.js";
|
|
4
|
+
import { FaviconSettings } from "./FaviconSettings.js";
|
|
5
|
+
import { ProjectNameSettings } from "./ProjectNameSettings.js";
|
|
6
|
+
import { SocialProfilesSettings } from "./SocialProfilesSettings.js";
|
|
7
|
+
import { c } from "react/compiler-runtime";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { TriangleAlert } from "lucide-react";
|
|
11
|
+
import { Skeleton } from "@camox/ui/skeleton";
|
|
12
|
+
import { Alert, AlertDescription } from "@camox/ui/alert";
|
|
13
|
+
import { Separator } from "@camox/ui/separator";
|
|
14
|
+
|
|
15
|
+
//#region src/features/settings/CamoxSettings.tsx
|
|
16
|
+
/** The Settings Section's parts, top to bottom. Each saves on its own. */
|
|
17
|
+
const parts = [
|
|
18
|
+
{
|
|
19
|
+
id: "name",
|
|
20
|
+
Part: ProjectNameSettings
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "favicon",
|
|
24
|
+
Part: FaviconSettings
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "social-profiles",
|
|
28
|
+
Part: SocialProfilesSettings
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "analytics",
|
|
32
|
+
Part: AnalyticsSettings
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* The Settings Section (GLOSSARY): where members change Project Settings. They apply to Live and
|
|
37
|
+
* every draft as soon as they're saved, whichever draft the studio is viewing.
|
|
38
|
+
*/
|
|
39
|
+
const CamoxSettings = () => {
|
|
40
|
+
const $ = c(8);
|
|
41
|
+
const projectUuid = useProjectUuid();
|
|
42
|
+
let t0;
|
|
43
|
+
if ($[0] !== projectUuid) {
|
|
44
|
+
t0 = reads.project({ projectUuid });
|
|
45
|
+
$[0] = projectUuid;
|
|
46
|
+
$[1] = t0;
|
|
47
|
+
} else t0 = $[1];
|
|
48
|
+
const { data: project } = useReadQuery(t0);
|
|
49
|
+
let t1;
|
|
50
|
+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
51
|
+
t1 = /* @__PURE__ */ jsx("h1", {
|
|
52
|
+
className: "text-xl font-semibold",
|
|
53
|
+
children: "Settings"
|
|
54
|
+
});
|
|
55
|
+
$[2] = t1;
|
|
56
|
+
} else t1 = $[2];
|
|
57
|
+
let t2;
|
|
58
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
59
|
+
t2 = /* @__PURE__ */ jsxs("header", {
|
|
60
|
+
className: "flex flex-col gap-4",
|
|
61
|
+
children: [t1, /* @__PURE__ */ jsxs(Alert, {
|
|
62
|
+
variant: "warning",
|
|
63
|
+
children: [/* @__PURE__ */ jsx(TriangleAlert, {}), /* @__PURE__ */ jsx(AlertDescription, { children: "Settings apply to the live website and every draft as soon as you save them." })]
|
|
64
|
+
})]
|
|
65
|
+
});
|
|
66
|
+
$[3] = t2;
|
|
67
|
+
} else t2 = $[3];
|
|
68
|
+
let t3;
|
|
69
|
+
if ($[4] !== project) {
|
|
70
|
+
t3 = project ? parts.map((t4, index) => {
|
|
71
|
+
const { id, Part } = t4;
|
|
72
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [index > 0 && /* @__PURE__ */ jsx(Separator, {}), /* @__PURE__ */ jsx(Part, { project })] }, id);
|
|
73
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
74
|
+
role: "status",
|
|
75
|
+
"aria-label": "Loading settings",
|
|
76
|
+
className: "flex flex-col gap-4",
|
|
77
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-24" })]
|
|
78
|
+
});
|
|
79
|
+
$[4] = project;
|
|
80
|
+
$[5] = t3;
|
|
81
|
+
} else t3 = $[5];
|
|
82
|
+
let t4;
|
|
83
|
+
if ($[6] !== t3) {
|
|
84
|
+
t4 = /* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: "mx-auto flex w-full max-w-4xl flex-col gap-8 p-6 md:p-10",
|
|
86
|
+
children: [t2, t3]
|
|
87
|
+
});
|
|
88
|
+
$[6] = t3;
|
|
89
|
+
$[7] = t4;
|
|
90
|
+
} else t4 = $[7];
|
|
91
|
+
return t4;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { CamoxSettings };
|