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,254 @@
|
|
|
1
|
+
import { getApiUrl } from "../../lib/api-client.js";
|
|
2
|
+
import { reads, useStudioCache } from "../../lib/studio-reads.js";
|
|
3
|
+
import { buildDefaultFaviconUrl, buildFaviconUrl } from "../routes/pageRuntime.js";
|
|
4
|
+
import { SettingsPart } from "./SettingsPart.js";
|
|
5
|
+
import { useProjectMutation } from "./useProjectMutation.js";
|
|
6
|
+
import { c } from "react/compiler-runtime";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { Button } from "@camox/ui/button";
|
|
10
|
+
import { Trash2 } from "lucide-react";
|
|
11
|
+
import { toast } from "@camox/ui/toaster";
|
|
12
|
+
import { Spinner } from "@camox/ui/spinner";
|
|
13
|
+
import { FaviconDropZone, validateFaviconFile } from "@camox/ui/favicon-drop-zone";
|
|
14
|
+
|
|
15
|
+
//#region src/features/settings/FaviconSettings.tsx
|
|
16
|
+
/** Points the studio tab's own icon at the saved favicon, so the change shows without a reload. */
|
|
17
|
+
function refreshDocumentFavicon(href) {
|
|
18
|
+
for (const link of document.querySelectorAll("link[rel~=\"icon\"]")) link.href = href;
|
|
19
|
+
}
|
|
20
|
+
function FaviconSettings(t0) {
|
|
21
|
+
const $ = c(51);
|
|
22
|
+
const { project } = t0;
|
|
23
|
+
const studioCache = useStudioCache();
|
|
24
|
+
const [pending, setPending] = React.useState(null);
|
|
25
|
+
const [hasCurrent, setHasCurrent] = React.useState(false);
|
|
26
|
+
let t1;
|
|
27
|
+
if ($[0] !== project) {
|
|
28
|
+
t1 = buildFaviconUrl(getApiUrl(), project, { withDefault: false });
|
|
29
|
+
$[0] = project;
|
|
30
|
+
$[1] = t1;
|
|
31
|
+
} else t1 = $[1];
|
|
32
|
+
const currentUrl = t1;
|
|
33
|
+
let t2;
|
|
34
|
+
let t3;
|
|
35
|
+
if ($[2] !== currentUrl) {
|
|
36
|
+
t2 = () => {
|
|
37
|
+
const probe = new Image();
|
|
38
|
+
probe.onload = () => setHasCurrent(true);
|
|
39
|
+
probe.onerror = () => setHasCurrent(false);
|
|
40
|
+
probe.src = currentUrl;
|
|
41
|
+
return () => {
|
|
42
|
+
probe.onload = null;
|
|
43
|
+
probe.onerror = null;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
t3 = [currentUrl];
|
|
47
|
+
$[2] = currentUrl;
|
|
48
|
+
$[3] = t2;
|
|
49
|
+
$[4] = t3;
|
|
50
|
+
} else {
|
|
51
|
+
t2 = $[3];
|
|
52
|
+
t3 = $[4];
|
|
53
|
+
}
|
|
54
|
+
React.useEffect(t2, t3);
|
|
55
|
+
const pendingFile = pending?.type === "upload" ? pending.file : null;
|
|
56
|
+
let t4;
|
|
57
|
+
if ($[5] !== pendingFile) {
|
|
58
|
+
t4 = pendingFile ? URL.createObjectURL(pendingFile) : null;
|
|
59
|
+
$[5] = pendingFile;
|
|
60
|
+
$[6] = t4;
|
|
61
|
+
} else t4 = $[6];
|
|
62
|
+
const pendingUrl = t4;
|
|
63
|
+
let t5;
|
|
64
|
+
let t6;
|
|
65
|
+
if ($[7] !== pendingUrl) {
|
|
66
|
+
t5 = () => {
|
|
67
|
+
if (!pendingUrl) return;
|
|
68
|
+
return () => URL.revokeObjectURL(pendingUrl);
|
|
69
|
+
};
|
|
70
|
+
t6 = [pendingUrl];
|
|
71
|
+
$[7] = pendingUrl;
|
|
72
|
+
$[8] = t5;
|
|
73
|
+
$[9] = t6;
|
|
74
|
+
} else {
|
|
75
|
+
t5 = $[8];
|
|
76
|
+
t6 = $[9];
|
|
77
|
+
}
|
|
78
|
+
React.useEffect(t5, t6);
|
|
79
|
+
let t7;
|
|
80
|
+
if ($[10] !== project || $[11] !== studioCache) {
|
|
81
|
+
t7 = () => {
|
|
82
|
+
const updated = studioCache.get(reads.project({ projectUuid: project.uuid }));
|
|
83
|
+
refreshDocumentFavicon(buildFaviconUrl(getApiUrl(), updated ?? project));
|
|
84
|
+
setPending(null);
|
|
85
|
+
};
|
|
86
|
+
$[10] = project;
|
|
87
|
+
$[11] = studioCache;
|
|
88
|
+
$[12] = t7;
|
|
89
|
+
} else t7 = $[12];
|
|
90
|
+
const onSuccess = t7;
|
|
91
|
+
let t8;
|
|
92
|
+
if ($[13] !== onSuccess) {
|
|
93
|
+
t8 = {
|
|
94
|
+
success: "Favicon updated",
|
|
95
|
+
failure: "Could not save the favicon",
|
|
96
|
+
onSuccess
|
|
97
|
+
};
|
|
98
|
+
$[13] = onSuccess;
|
|
99
|
+
$[14] = t8;
|
|
100
|
+
} else t8 = $[14];
|
|
101
|
+
const upload = useProjectMutation(project, "uploadFavicon", t8);
|
|
102
|
+
let t9;
|
|
103
|
+
if ($[15] !== onSuccess) {
|
|
104
|
+
t9 = {
|
|
105
|
+
success: "Favicon removed",
|
|
106
|
+
failure: "Could not save the favicon",
|
|
107
|
+
onSuccess
|
|
108
|
+
};
|
|
109
|
+
$[15] = onSuccess;
|
|
110
|
+
$[16] = t9;
|
|
111
|
+
} else t9 = $[16];
|
|
112
|
+
const remove = useProjectMutation(project, "deleteFavicon", t9);
|
|
113
|
+
const isSaving = upload.isPending || remove.isPending;
|
|
114
|
+
let t10;
|
|
115
|
+
if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
|
|
116
|
+
t10 = (file) => {
|
|
117
|
+
const error = validateFaviconFile(file);
|
|
118
|
+
if (error) {
|
|
119
|
+
toast.error(error);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
setPending({
|
|
123
|
+
type: "upload",
|
|
124
|
+
file
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
$[17] = t10;
|
|
128
|
+
} else t10 = $[17];
|
|
129
|
+
const choose = t10;
|
|
130
|
+
let t11;
|
|
131
|
+
if ($[18] !== pending || $[19] !== project || $[20] !== remove || $[21] !== upload) {
|
|
132
|
+
t11 = () => {
|
|
133
|
+
if (pending?.type === "upload") {
|
|
134
|
+
upload.mutate({
|
|
135
|
+
projectId: project.id,
|
|
136
|
+
file: pending.file
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (pending?.type === "remove") remove.mutate({ projectId: project.id });
|
|
141
|
+
};
|
|
142
|
+
$[18] = pending;
|
|
143
|
+
$[19] = project;
|
|
144
|
+
$[20] = remove;
|
|
145
|
+
$[21] = upload;
|
|
146
|
+
$[22] = t11;
|
|
147
|
+
} else t11 = $[22];
|
|
148
|
+
const save = t11;
|
|
149
|
+
const showsFavicon = pending ? pending.type === "upload" : hasCurrent;
|
|
150
|
+
let t12;
|
|
151
|
+
if ($[23] !== currentUrl || $[24] !== pendingUrl || $[25] !== showsFavicon) {
|
|
152
|
+
t12 = pendingUrl ?? (showsFavicon ? currentUrl : buildDefaultFaviconUrl(getApiUrl()));
|
|
153
|
+
$[23] = currentUrl;
|
|
154
|
+
$[24] = pendingUrl;
|
|
155
|
+
$[25] = showsFavicon;
|
|
156
|
+
$[26] = t12;
|
|
157
|
+
} else t12 = $[26];
|
|
158
|
+
const previewUrl = t12;
|
|
159
|
+
const prompt = pendingFile ? pendingFile.name : showsFavicon ? "Drop a new favicon, or click to browse" : "Drop your favicon here, or click to browse";
|
|
160
|
+
let t13;
|
|
161
|
+
if ($[27] !== isSaving || $[28] !== previewUrl || $[29] !== prompt) {
|
|
162
|
+
t13 = /* @__PURE__ */ jsx(FaviconDropZone, {
|
|
163
|
+
previewUrl,
|
|
164
|
+
prompt,
|
|
165
|
+
disabled: isSaving,
|
|
166
|
+
onFileSelected: choose
|
|
167
|
+
});
|
|
168
|
+
$[27] = isSaving;
|
|
169
|
+
$[28] = previewUrl;
|
|
170
|
+
$[29] = prompt;
|
|
171
|
+
$[30] = t13;
|
|
172
|
+
} else t13 = $[30];
|
|
173
|
+
const t14 = !pending || isSaving;
|
|
174
|
+
let t15;
|
|
175
|
+
if ($[31] !== isSaving) {
|
|
176
|
+
t15 = isSaving && /* @__PURE__ */ jsx(Spinner, {});
|
|
177
|
+
$[31] = isSaving;
|
|
178
|
+
$[32] = t15;
|
|
179
|
+
} else t15 = $[32];
|
|
180
|
+
let t16;
|
|
181
|
+
if ($[33] !== save || $[34] !== t14 || $[35] !== t15) {
|
|
182
|
+
t16 = /* @__PURE__ */ jsxs(Button, {
|
|
183
|
+
type: "button",
|
|
184
|
+
onClick: save,
|
|
185
|
+
disabled: t14,
|
|
186
|
+
children: [t15, "Save"]
|
|
187
|
+
});
|
|
188
|
+
$[33] = save;
|
|
189
|
+
$[34] = t14;
|
|
190
|
+
$[35] = t15;
|
|
191
|
+
$[36] = t16;
|
|
192
|
+
} else t16 = $[36];
|
|
193
|
+
let t17;
|
|
194
|
+
if ($[37] !== isSaving || $[38] !== pending) {
|
|
195
|
+
t17 = pending && /* @__PURE__ */ jsx(Button, {
|
|
196
|
+
type: "button",
|
|
197
|
+
variant: "outline",
|
|
198
|
+
disabled: isSaving,
|
|
199
|
+
onClick: () => setPending(null),
|
|
200
|
+
children: "Cancel"
|
|
201
|
+
});
|
|
202
|
+
$[37] = isSaving;
|
|
203
|
+
$[38] = pending;
|
|
204
|
+
$[39] = t17;
|
|
205
|
+
} else t17 = $[39];
|
|
206
|
+
let t18;
|
|
207
|
+
if ($[40] !== hasCurrent || $[41] !== isSaving || $[42] !== showsFavicon) {
|
|
208
|
+
t18 = showsFavicon && /* @__PURE__ */ jsxs(Button, {
|
|
209
|
+
type: "button",
|
|
210
|
+
variant: "ghost",
|
|
211
|
+
className: "ml-auto",
|
|
212
|
+
disabled: isSaving,
|
|
213
|
+
onClick: () => setPending(hasCurrent ? { type: "remove" } : null),
|
|
214
|
+
children: [/* @__PURE__ */ jsx(Trash2, {}), "Remove favicon"]
|
|
215
|
+
});
|
|
216
|
+
$[40] = hasCurrent;
|
|
217
|
+
$[41] = isSaving;
|
|
218
|
+
$[42] = showsFavicon;
|
|
219
|
+
$[43] = t18;
|
|
220
|
+
} else t18 = $[43];
|
|
221
|
+
let t19;
|
|
222
|
+
if ($[44] !== t16 || $[45] !== t17 || $[46] !== t18) {
|
|
223
|
+
t19 = /* @__PURE__ */ jsxs("div", {
|
|
224
|
+
className: "flex flex-wrap gap-2",
|
|
225
|
+
children: [
|
|
226
|
+
t16,
|
|
227
|
+
t17,
|
|
228
|
+
t18
|
|
229
|
+
]
|
|
230
|
+
});
|
|
231
|
+
$[44] = t16;
|
|
232
|
+
$[45] = t17;
|
|
233
|
+
$[46] = t18;
|
|
234
|
+
$[47] = t19;
|
|
235
|
+
} else t19 = $[47];
|
|
236
|
+
let t20;
|
|
237
|
+
if ($[48] !== t13 || $[49] !== t19) {
|
|
238
|
+
t20 = /* @__PURE__ */ jsx(SettingsPart, {
|
|
239
|
+
title: "Favicon",
|
|
240
|
+
description: "The icon shown in browser tabs and bookmarks.",
|
|
241
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
242
|
+
className: "flex flex-col gap-4",
|
|
243
|
+
children: [t13, t19]
|
|
244
|
+
})
|
|
245
|
+
});
|
|
246
|
+
$[48] = t13;
|
|
247
|
+
$[49] = t19;
|
|
248
|
+
$[50] = t20;
|
|
249
|
+
} else t20 = $[50];
|
|
250
|
+
return t20;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
export { FaviconSettings };
|
|
@@ -0,0 +1,145 @@
|
|
|
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 { 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 { Spinner } from "@camox/ui/spinner";
|
|
10
|
+
|
|
11
|
+
//#region src/features/settings/ProjectNameSettings.tsx
|
|
12
|
+
function ProjectNameSettings(t0) {
|
|
13
|
+
const $ = c(27);
|
|
14
|
+
const { project } = t0;
|
|
15
|
+
const [name, setName] = React.useState(project.name);
|
|
16
|
+
const [error, setError] = React.useState(null);
|
|
17
|
+
let t1;
|
|
18
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
19
|
+
t1 = {
|
|
20
|
+
success: "Project name updated",
|
|
21
|
+
failure: "Could not update the project name"
|
|
22
|
+
};
|
|
23
|
+
$[0] = t1;
|
|
24
|
+
} else t1 = $[0];
|
|
25
|
+
const save = useProjectMutation(project, "update", t1);
|
|
26
|
+
let t2;
|
|
27
|
+
if ($[1] !== name) {
|
|
28
|
+
t2 = name.trim();
|
|
29
|
+
$[1] = name;
|
|
30
|
+
$[2] = t2;
|
|
31
|
+
} else t2 = $[2];
|
|
32
|
+
const trimmedName = t2;
|
|
33
|
+
let t3;
|
|
34
|
+
if ($[3] !== project.id || $[4] !== save || $[5] !== trimmedName) {
|
|
35
|
+
t3 = (event) => {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
if (!trimmedName) {
|
|
38
|
+
setError("Project name is required");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
save.mutate({
|
|
42
|
+
id: project.id,
|
|
43
|
+
name: trimmedName
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
$[3] = project.id;
|
|
47
|
+
$[4] = save;
|
|
48
|
+
$[5] = trimmedName;
|
|
49
|
+
$[6] = t3;
|
|
50
|
+
} else t3 = $[6];
|
|
51
|
+
const submit = t3;
|
|
52
|
+
let t4;
|
|
53
|
+
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
|
54
|
+
t4 = /* @__PURE__ */ jsx(Label, {
|
|
55
|
+
htmlFor: "camox-project-name",
|
|
56
|
+
children: "Project name"
|
|
57
|
+
});
|
|
58
|
+
$[7] = t4;
|
|
59
|
+
} else t4 = $[7];
|
|
60
|
+
const t5 = error ? true : void 0;
|
|
61
|
+
let t6;
|
|
62
|
+
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
|
63
|
+
t6 = (event_0) => {
|
|
64
|
+
setName(event_0.target.value);
|
|
65
|
+
setError(null);
|
|
66
|
+
};
|
|
67
|
+
$[8] = t6;
|
|
68
|
+
} else t6 = $[8];
|
|
69
|
+
let t7;
|
|
70
|
+
if ($[9] !== name || $[10] !== save.isPending || $[11] !== t5) {
|
|
71
|
+
t7 = /* @__PURE__ */ jsx(Input, {
|
|
72
|
+
id: "camox-project-name",
|
|
73
|
+
value: name,
|
|
74
|
+
"aria-invalid": t5,
|
|
75
|
+
disabled: save.isPending,
|
|
76
|
+
onChange: t6
|
|
77
|
+
});
|
|
78
|
+
$[9] = name;
|
|
79
|
+
$[10] = save.isPending;
|
|
80
|
+
$[11] = t5;
|
|
81
|
+
$[12] = t7;
|
|
82
|
+
} else t7 = $[12];
|
|
83
|
+
let t8;
|
|
84
|
+
if ($[13] !== error) {
|
|
85
|
+
t8 = error && /* @__PURE__ */ jsx("p", {
|
|
86
|
+
className: "text-destructive text-xs",
|
|
87
|
+
children: error
|
|
88
|
+
});
|
|
89
|
+
$[13] = error;
|
|
90
|
+
$[14] = t8;
|
|
91
|
+
} else t8 = $[14];
|
|
92
|
+
let t9;
|
|
93
|
+
if ($[15] !== t7 || $[16] !== t8) {
|
|
94
|
+
t9 = /* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "flex flex-col gap-2",
|
|
96
|
+
children: [
|
|
97
|
+
t4,
|
|
98
|
+
t7,
|
|
99
|
+
t8
|
|
100
|
+
]
|
|
101
|
+
});
|
|
102
|
+
$[15] = t7;
|
|
103
|
+
$[16] = t8;
|
|
104
|
+
$[17] = t9;
|
|
105
|
+
} else t9 = $[17];
|
|
106
|
+
const t10 = trimmedName === project.name || save.isPending;
|
|
107
|
+
let t11;
|
|
108
|
+
if ($[18] !== save.isPending) {
|
|
109
|
+
t11 = save.isPending && /* @__PURE__ */ jsx(Spinner, {});
|
|
110
|
+
$[18] = save.isPending;
|
|
111
|
+
$[19] = t11;
|
|
112
|
+
} else t11 = $[19];
|
|
113
|
+
let t12;
|
|
114
|
+
if ($[20] !== t10 || $[21] !== t11) {
|
|
115
|
+
t12 = /* @__PURE__ */ jsxs(Button, {
|
|
116
|
+
type: "submit",
|
|
117
|
+
className: "self-start",
|
|
118
|
+
disabled: t10,
|
|
119
|
+
children: [t11, "Save changes"]
|
|
120
|
+
});
|
|
121
|
+
$[20] = t10;
|
|
122
|
+
$[21] = t11;
|
|
123
|
+
$[22] = t12;
|
|
124
|
+
} else t12 = $[22];
|
|
125
|
+
let t13;
|
|
126
|
+
if ($[23] !== submit || $[24] !== t12 || $[25] !== t9) {
|
|
127
|
+
t13 = /* @__PURE__ */ jsx(SettingsPart, {
|
|
128
|
+
title: "Project name",
|
|
129
|
+
description: "How the project is named in Camox.",
|
|
130
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
131
|
+
onSubmit: submit,
|
|
132
|
+
className: "flex flex-col gap-4",
|
|
133
|
+
children: [t9, t12]
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
$[23] = submit;
|
|
137
|
+
$[24] = t12;
|
|
138
|
+
$[25] = t9;
|
|
139
|
+
$[26] = t13;
|
|
140
|
+
} else t13 = $[26];
|
|
141
|
+
return t13;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
export { ProjectNameSettings };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { c } from "react/compiler-runtime";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/features/settings/SettingsPart.tsx
|
|
5
|
+
/** One part of the Settings Section: what it sets, beside the form that sets it. */
|
|
6
|
+
function SettingsPart(t0) {
|
|
7
|
+
const $ = c(17);
|
|
8
|
+
const { title, description, children } = t0;
|
|
9
|
+
let t1;
|
|
10
|
+
if ($[0] !== title) {
|
|
11
|
+
let t2;
|
|
12
|
+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
13
|
+
t2 = /\W+/g;
|
|
14
|
+
$[2] = t2;
|
|
15
|
+
} else t2 = $[2];
|
|
16
|
+
t1 = title.toLowerCase().replaceAll(t2, "-");
|
|
17
|
+
$[0] = title;
|
|
18
|
+
$[1] = t1;
|
|
19
|
+
} else t1 = $[1];
|
|
20
|
+
const id = `camox-settings-${t1}`;
|
|
21
|
+
let t2;
|
|
22
|
+
if ($[3] !== id || $[4] !== title) {
|
|
23
|
+
t2 = /* @__PURE__ */ jsx("h2", {
|
|
24
|
+
id,
|
|
25
|
+
className: "font-semibold",
|
|
26
|
+
children: title
|
|
27
|
+
});
|
|
28
|
+
$[3] = id;
|
|
29
|
+
$[4] = title;
|
|
30
|
+
$[5] = t2;
|
|
31
|
+
} else t2 = $[5];
|
|
32
|
+
let t3;
|
|
33
|
+
if ($[6] !== description) {
|
|
34
|
+
t3 = /* @__PURE__ */ jsx("p", {
|
|
35
|
+
className: "text-muted-foreground mt-1 text-sm",
|
|
36
|
+
children: description
|
|
37
|
+
});
|
|
38
|
+
$[6] = description;
|
|
39
|
+
$[7] = t3;
|
|
40
|
+
} else t3 = $[7];
|
|
41
|
+
let t4;
|
|
42
|
+
if ($[8] !== t2 || $[9] !== t3) {
|
|
43
|
+
t4 = /* @__PURE__ */ jsxs("div", { children: [t2, t3] });
|
|
44
|
+
$[8] = t2;
|
|
45
|
+
$[9] = t3;
|
|
46
|
+
$[10] = t4;
|
|
47
|
+
} else t4 = $[10];
|
|
48
|
+
let t5;
|
|
49
|
+
if ($[11] !== children) {
|
|
50
|
+
t5 = /* @__PURE__ */ jsx("div", {
|
|
51
|
+
className: "md:col-span-2",
|
|
52
|
+
children
|
|
53
|
+
});
|
|
54
|
+
$[11] = children;
|
|
55
|
+
$[12] = t5;
|
|
56
|
+
} else t5 = $[12];
|
|
57
|
+
let t6;
|
|
58
|
+
if ($[13] !== id || $[14] !== t4 || $[15] !== t5) {
|
|
59
|
+
t6 = /* @__PURE__ */ jsxs("section", {
|
|
60
|
+
"aria-labelledby": id,
|
|
61
|
+
className: "grid gap-4 md:grid-cols-3 md:gap-8",
|
|
62
|
+
children: [t4, t5]
|
|
63
|
+
});
|
|
64
|
+
$[13] = id;
|
|
65
|
+
$[14] = t4;
|
|
66
|
+
$[15] = t5;
|
|
67
|
+
$[16] = t6;
|
|
68
|
+
} else t6 = $[16];
|
|
69
|
+
return t6;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { SettingsPart };
|
|
@@ -0,0 +1,245 @@
|
|
|
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 { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Button } from "@camox/ui/button";
|
|
7
|
+
import { Input } from "@camox/ui/input";
|
|
8
|
+
import { ArrowDown, ArrowUp, Plus, Trash2 } from "lucide-react";
|
|
9
|
+
import { Spinner } from "@camox/ui/spinner";
|
|
10
|
+
import { socialProfileUrlSchema } from "@camox/api-contract/projects";
|
|
11
|
+
|
|
12
|
+
//#region src/features/settings/SocialProfilesSettings.tsx
|
|
13
|
+
let nextRowKey = 0;
|
|
14
|
+
const row = (url) => ({
|
|
15
|
+
key: nextRowKey++,
|
|
16
|
+
url
|
|
17
|
+
});
|
|
18
|
+
function SocialProfilesSettings(t0) {
|
|
19
|
+
const $ = c(33);
|
|
20
|
+
const { project } = t0;
|
|
21
|
+
let t1;
|
|
22
|
+
if ($[0] !== project) {
|
|
23
|
+
t1 = () => project.socialProfiles.map(row);
|
|
24
|
+
$[0] = project;
|
|
25
|
+
$[1] = t1;
|
|
26
|
+
} else t1 = $[1];
|
|
27
|
+
const [rows, setRows] = React.useState(t1);
|
|
28
|
+
const [showErrors, setShowErrors] = React.useState(false);
|
|
29
|
+
let t2;
|
|
30
|
+
if ($[2] !== rows) {
|
|
31
|
+
t2 = rows.map(_temp).filter(_temp2);
|
|
32
|
+
$[2] = rows;
|
|
33
|
+
$[3] = t2;
|
|
34
|
+
} else t2 = $[3];
|
|
35
|
+
const filled = t2;
|
|
36
|
+
let t3;
|
|
37
|
+
if ($[4] !== filled) {
|
|
38
|
+
t3 = {
|
|
39
|
+
success: "Social profiles updated",
|
|
40
|
+
failure: "Could not save the social profiles",
|
|
41
|
+
onSuccess: () => {
|
|
42
|
+
setRows(filled);
|
|
43
|
+
setShowErrors(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
$[4] = filled;
|
|
47
|
+
$[5] = t3;
|
|
48
|
+
} else t3 = $[5];
|
|
49
|
+
const save = useProjectMutation(project, "updateSocialProfiles", t3);
|
|
50
|
+
const errorOf = _temp3;
|
|
51
|
+
let t4;
|
|
52
|
+
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
|
53
|
+
t4 = (key, url_0) => setRows((current) => current.map((entry_0) => entry_0.key === key ? {
|
|
54
|
+
...entry_0,
|
|
55
|
+
url: url_0
|
|
56
|
+
} : entry_0));
|
|
57
|
+
$[6] = t4;
|
|
58
|
+
} else t4 = $[6];
|
|
59
|
+
const update = t4;
|
|
60
|
+
let t5;
|
|
61
|
+
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
|
62
|
+
t5 = (key_0) => setRows((current_0) => current_0.filter((entry_1) => entry_1.key !== key_0));
|
|
63
|
+
$[7] = t5;
|
|
64
|
+
} else t5 = $[7];
|
|
65
|
+
const remove = t5;
|
|
66
|
+
let t6;
|
|
67
|
+
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
|
68
|
+
t6 = (index, by) => setRows((current_1) => {
|
|
69
|
+
const next = [...current_1];
|
|
70
|
+
const [moved] = next.splice(index, 1);
|
|
71
|
+
next.splice(index + by, 0, moved);
|
|
72
|
+
return next;
|
|
73
|
+
});
|
|
74
|
+
$[8] = t6;
|
|
75
|
+
} else t6 = $[8];
|
|
76
|
+
const move = t6;
|
|
77
|
+
let t7;
|
|
78
|
+
if ($[9] !== filled || $[10] !== project || $[11] !== save) {
|
|
79
|
+
t7 = (event) => {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
if (filled.some((entry_2) => errorOf(entry_2.url))) {
|
|
82
|
+
setShowErrors(true);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
save.mutate({
|
|
86
|
+
id: project.id,
|
|
87
|
+
socialProfiles: filled.map(_temp4)
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
$[9] = filled;
|
|
91
|
+
$[10] = project;
|
|
92
|
+
$[11] = save;
|
|
93
|
+
$[12] = t7;
|
|
94
|
+
} else t7 = $[12];
|
|
95
|
+
const submit = t7;
|
|
96
|
+
let t8;
|
|
97
|
+
if ($[13] !== rows || $[14] !== save.isPending || $[15] !== showErrors) {
|
|
98
|
+
t8 = rows.length > 0 && /* @__PURE__ */ jsx("ol", {
|
|
99
|
+
className: "flex flex-col gap-2",
|
|
100
|
+
children: rows.map((entry_4, index_0) => {
|
|
101
|
+
const error = showErrors && entry_4.url.trim() ? errorOf(entry_4.url) : void 0;
|
|
102
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
103
|
+
className: "flex flex-col gap-1",
|
|
104
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
105
|
+
className: "flex items-center gap-1",
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Input, {
|
|
108
|
+
type: "url",
|
|
109
|
+
value: entry_4.url,
|
|
110
|
+
placeholder: "https://www.linkedin.com/company/…",
|
|
111
|
+
"aria-label": `Profile ${index_0 + 1}`,
|
|
112
|
+
"aria-invalid": error ? true : void 0,
|
|
113
|
+
disabled: save.isPending,
|
|
114
|
+
onChange: (event_0) => update(entry_4.key, event_0.target.value)
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ jsx(Button, {
|
|
117
|
+
type: "button",
|
|
118
|
+
variant: "ghost",
|
|
119
|
+
size: "icon",
|
|
120
|
+
"aria-label": "Move up",
|
|
121
|
+
disabled: index_0 === 0 || save.isPending,
|
|
122
|
+
onClick: () => move(index_0, -1),
|
|
123
|
+
children: /* @__PURE__ */ jsx(ArrowUp, {})
|
|
124
|
+
}),
|
|
125
|
+
/* @__PURE__ */ jsx(Button, {
|
|
126
|
+
type: "button",
|
|
127
|
+
variant: "ghost",
|
|
128
|
+
size: "icon",
|
|
129
|
+
"aria-label": "Move down",
|
|
130
|
+
disabled: index_0 === rows.length - 1 || save.isPending,
|
|
131
|
+
onClick: () => move(index_0, 1),
|
|
132
|
+
children: /* @__PURE__ */ jsx(ArrowDown, {})
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ jsx(Button, {
|
|
135
|
+
type: "button",
|
|
136
|
+
variant: "ghost",
|
|
137
|
+
size: "icon",
|
|
138
|
+
"aria-label": "Remove",
|
|
139
|
+
disabled: save.isPending,
|
|
140
|
+
onClick: () => remove(entry_4.key),
|
|
141
|
+
children: /* @__PURE__ */ jsx(Trash2, {})
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
}), error && /* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "text-destructive text-xs",
|
|
146
|
+
children: error
|
|
147
|
+
})]
|
|
148
|
+
}, entry_4.key);
|
|
149
|
+
})
|
|
150
|
+
});
|
|
151
|
+
$[13] = rows;
|
|
152
|
+
$[14] = save.isPending;
|
|
153
|
+
$[15] = showErrors;
|
|
154
|
+
$[16] = t8;
|
|
155
|
+
} else t8 = $[16];
|
|
156
|
+
let t9;
|
|
157
|
+
if ($[17] !== save.isPending) {
|
|
158
|
+
t9 = save.isPending && /* @__PURE__ */ jsx(Spinner, {});
|
|
159
|
+
$[17] = save.isPending;
|
|
160
|
+
$[18] = t9;
|
|
161
|
+
} else t9 = $[18];
|
|
162
|
+
let t10;
|
|
163
|
+
if ($[19] !== save.isPending || $[20] !== t9) {
|
|
164
|
+
t10 = /* @__PURE__ */ jsxs(Button, {
|
|
165
|
+
type: "submit",
|
|
166
|
+
disabled: save.isPending,
|
|
167
|
+
children: [t9, "Save"]
|
|
168
|
+
});
|
|
169
|
+
$[19] = save.isPending;
|
|
170
|
+
$[20] = t9;
|
|
171
|
+
$[21] = t10;
|
|
172
|
+
} else t10 = $[21];
|
|
173
|
+
let t11;
|
|
174
|
+
let t12;
|
|
175
|
+
if ($[22] === Symbol.for("react.memo_cache_sentinel")) {
|
|
176
|
+
t11 = () => setRows(_temp5);
|
|
177
|
+
t12 = /* @__PURE__ */ jsx(Plus, {});
|
|
178
|
+
$[22] = t11;
|
|
179
|
+
$[23] = t12;
|
|
180
|
+
} else {
|
|
181
|
+
t11 = $[22];
|
|
182
|
+
t12 = $[23];
|
|
183
|
+
}
|
|
184
|
+
let t13;
|
|
185
|
+
if ($[24] !== save.isPending) {
|
|
186
|
+
t13 = /* @__PURE__ */ jsxs(Button, {
|
|
187
|
+
type: "button",
|
|
188
|
+
variant: "ghost",
|
|
189
|
+
disabled: save.isPending,
|
|
190
|
+
onClick: t11,
|
|
191
|
+
children: [t12, "Add profile"]
|
|
192
|
+
});
|
|
193
|
+
$[24] = save.isPending;
|
|
194
|
+
$[25] = t13;
|
|
195
|
+
} else t13 = $[25];
|
|
196
|
+
let t14;
|
|
197
|
+
if ($[26] !== t10 || $[27] !== t13) {
|
|
198
|
+
t14 = /* @__PURE__ */ jsxs("div", {
|
|
199
|
+
className: "flex flex-wrap gap-2",
|
|
200
|
+
children: [t10, t13]
|
|
201
|
+
});
|
|
202
|
+
$[26] = t10;
|
|
203
|
+
$[27] = t13;
|
|
204
|
+
$[28] = t14;
|
|
205
|
+
} else t14 = $[28];
|
|
206
|
+
let t15;
|
|
207
|
+
if ($[29] !== submit || $[30] !== t14 || $[31] !== t8) {
|
|
208
|
+
t15 = /* @__PURE__ */ jsx(SettingsPart, {
|
|
209
|
+
title: "Social profiles",
|
|
210
|
+
description: "The project's accounts elsewhere, such as on LinkedIn, X or GitHub. Search engines and AI answer engines use them to tie the website to your organization.",
|
|
211
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
212
|
+
onSubmit: submit,
|
|
213
|
+
className: "flex flex-col gap-4",
|
|
214
|
+
children: [t8, t14]
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
$[29] = submit;
|
|
218
|
+
$[30] = t14;
|
|
219
|
+
$[31] = t8;
|
|
220
|
+
$[32] = t15;
|
|
221
|
+
} else t15 = $[32];
|
|
222
|
+
return t15;
|
|
223
|
+
}
|
|
224
|
+
function _temp5(current_2) {
|
|
225
|
+
return [...current_2, row("")];
|
|
226
|
+
}
|
|
227
|
+
function _temp4(entry_3) {
|
|
228
|
+
return entry_3.url;
|
|
229
|
+
}
|
|
230
|
+
function _temp3(url) {
|
|
231
|
+
const result = socialProfileUrlSchema.safeParse(url.trim());
|
|
232
|
+
return result.success ? void 0 : result.error.issues[0]?.message;
|
|
233
|
+
}
|
|
234
|
+
function _temp2(e) {
|
|
235
|
+
return e.url;
|
|
236
|
+
}
|
|
237
|
+
function _temp(entry) {
|
|
238
|
+
return {
|
|
239
|
+
...entry,
|
|
240
|
+
url: entry.url.trim()
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
//#endregion
|
|
245
|
+
export { SocialProfilesSettings };
|