forgecad 0.6.3 → 0.7.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/README.md +2 -11
- package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-DAu1C1ST.js} +250 -151
- package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
- package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-Gc_BCdqC.js} +269 -143
- package/dist/assets/EditorApp-D9bJvtf7.js +11338 -0
- package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-DG1-oUSV.css} +459 -87
- package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-CEO8XbV8.js} +2 -4
- package/dist/assets/LandingPage-CdCuEOdC.js +451 -0
- package/dist/assets/PricingPage-BSrxu6d7.js +232 -0
- package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-FUCSIRq6.js} +129 -5
- package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-KoR0SNKq.js} +6770 -2914
- package/dist/assets/{index-2hfs_ub0.css → index-CyVd1D4D.css} +227 -53
- package/dist/assets/{Viewport-CoB46f5R.js → index-wTEK39at.js} +31385 -6439
- package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
- package/dist/assets/{manifold-CqNMHHKO.js → manifold-B1sGWdYk.js} +4 -3
- package/dist/assets/{manifold-Cce9wRFz.js → manifold-D7o0N50J.js} +1 -1
- package/dist/assets/{manifold-D6BeHIOo.js → manifold-G5sBaXzi.js} +1 -1
- package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-DYcRHhv9.js} +6798 -3341
- package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
- package/dist/docs-raw/generated/assembly.md +691 -112
- package/dist/docs-raw/generated/concepts.md +1225 -1400
- package/dist/docs-raw/generated/core.md +464 -1412
- package/dist/docs-raw/generated/curves.md +593 -117
- package/dist/docs-raw/generated/lib.md +38 -748
- package/dist/docs-raw/generated/output.md +139 -245
- package/dist/docs-raw/generated/sheet-metal.md +473 -21
- package/dist/docs-raw/generated/sketch.md +553 -349
- package/dist/docs-raw/generated/viewport.md +345 -303
- package/dist/docs-raw/generated/wood.md +104 -0
- package/dist/index.html +2 -2
- package/dist/sitemap.xml +6 -6
- package/dist-cli/chunk-PZ5AY32C.js +10 -0
- package/dist-cli/chunk-PZ5AY32C.js.map +1 -0
- package/dist-cli/forgecad.js +9435 -5407
- package/dist-cli/forgecad.js.map +1 -0
- package/dist-cli/solver-FV7TJZGI.js +365 -0
- package/dist-cli/solver-FV7TJZGI.js.map +1 -0
- package/dist-skill/CONTEXT.md +3186 -7145
- package/dist-skill/SKILL-dev.md +21 -63
- package/dist-skill/SKILL.md +12 -56
- package/dist-skill/docs/API/core/concepts.md +16 -98
- package/dist-skill/docs/CLI/export.md +91 -0
- package/dist-skill/docs/CLI/projects.md +107 -0
- package/dist-skill/docs/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs/CLI/validation.md +66 -0
- package/dist-skill/docs/generated/assembly.md +691 -112
- package/dist-skill/docs/generated/core.md +464 -1412
- package/dist-skill/docs/generated/curves.md +593 -117
- package/dist-skill/docs/generated/lib.md +38 -748
- package/dist-skill/docs/generated/output.md +139 -245
- package/dist-skill/docs/generated/sheet-metal.md +473 -21
- package/dist-skill/docs/generated/sketch.md +553 -349
- package/dist-skill/docs/generated/viewport.md +345 -303
- package/dist-skill/docs/generated/wood.md +104 -0
- package/dist-skill/docs/guides/coordinate-system.md +11 -17
- package/dist-skill/docs/guides/geometry-conventions.md +13 -70
- package/dist-skill/docs/guides/modeling-recipes.md +22 -195
- package/dist-skill/docs/guides/positioning.md +88 -147
- package/dist-skill/docs-dev/API/core/concepts.md +51 -0
- package/dist-skill/docs-dev/API/core/sdf-advanced.md +92 -0
- package/dist-skill/docs-dev/API/core/sdf-primitives.md +58 -0
- package/dist-skill/docs-dev/API/core/sdf-workflow.md +42 -0
- package/dist-skill/docs-dev/CLI/export.md +91 -0
- package/dist-skill/docs-dev/CLI/projects.md +107 -0
- package/dist-skill/docs-dev/CLI/studio_publishing.md +52 -0
- package/dist-skill/docs-dev/CLI/validation.md +66 -0
- package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
- package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
- package/dist-skill/{docs → docs-dev}/coding.md +1 -3
- package/dist-skill/docs-dev/generated/assembly.md +771 -0
- package/dist-skill/docs-dev/generated/core.md +775 -0
- package/dist-skill/docs-dev/generated/curves.md +688 -0
- package/dist-skill/docs-dev/generated/lib.md +50 -0
- package/dist-skill/docs-dev/generated/output.md +234 -0
- package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
- package/dist-skill/docs-dev/generated/sketch.md +801 -0
- package/dist-skill/docs-dev/generated/viewport.md +486 -0
- package/dist-skill/docs-dev/generated/wood.md +104 -0
- package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
- package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
- package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
- package/dist-skill/docs-dev/guides/positioning.md +151 -0
- package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
- package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
- package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
- package/examples/api/attachTo-basics.forge.js +5 -5
- package/examples/api/boolean-operations.forge.js +3 -3
- package/examples/api/bounding-box-visualizer.forge.js +2 -2
- package/examples/api/clone-duplicate.forge.js +1 -1
- package/examples/api/colors-union-vs-array.forge.js +6 -6
- package/examples/api/connector-assembly.forge.js +4 -4
- package/examples/api/connector-basics.forge.js +2 -2
- package/examples/api/extrude-options.forge.js +4 -10
- package/examples/api/feature-created-faces.forge.js +6 -10
- package/examples/api/fillet-showcase.forge.js +1 -1
- package/examples/api/folded-service-panel-cover.forge.js +2 -2
- package/examples/api/group-test.forge.js +1 -1
- package/examples/api/group-vs-union.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +4 -0
- package/examples/api/js-module-pillars.js +1 -1
- package/examples/api/js-module-scene.js +2 -2
- package/examples/api/mesh-import-slats.forge.js +1 -1
- package/examples/api/pointAlong-orientation.forge.js +1 -1
- package/examples/api/profile-2020-b-slot6.forge.js +0 -1
- package/examples/api/route-perimeter-flange.forge.js +1 -1
- package/examples/api/sdf-rover-demo.forge.js +10 -10
- package/examples/api/sketch-on-face-demo.forge.js +2 -2
- package/examples/api/sketch-regions.forge.js +4 -4
- package/examples/api/transition-curves.forge.js +1 -1
- package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
- package/examples/api/variable-sweep-test.forge.js +2 -2
- package/examples/api/wood-joinery.forge.js +60 -0
- package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
- package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
- package/examples/experiments/drone-arm.forge.js +53 -0
- package/examples/furniture/adjustable-table.forge.js +2 -2
- package/examples/furniture/bathroom.forge.js +11 -11
- package/examples/furniture/chair.forge.js +1 -1
- package/examples/generative/crystal-growth.forge.js +2 -2
- package/examples/generative/frost-spires.forge.js +3 -3
- package/examples/generative/golden-spiral-tower.forge.js +3 -3
- package/examples/mechanical/3d-printer.forge.js +28 -28
- package/examples/mechanical/5-finger-robot-hand.forge.js +15 -15
- package/examples/mechanical/airplane-propeller.forge.js +2 -2
- package/examples/mechanical/fillet-enclosure.forge.js +1 -1
- package/examples/mechanical/headphone-hanger-v2.forge.js +2 -2
- package/examples/mechanical/robot_hand.forge.js +15 -15
- package/examples/mechanical/robot_hand_2.forge.js +9 -9
- package/examples/products/bottle.forge.js +1 -1
- package/examples/products/chess-set.forge.js +19 -19
- package/examples/products/classical-piano.forge.js +11 -11
- package/examples/products/clock.forge.js +12 -12
- package/examples/products/iphone.forge.js +8 -8
- package/examples/products/laptop.forge.js +15 -15
- package/examples/products/liquid-soap-dispenser.forge.js +18 -18
- package/examples/products/origami-fish.forge.js +8 -6
- package/examples/products/spiderman-cake.forge.js +4 -4
- package/examples/toolbox/bolted-joint.forge.js +2 -2
- package/package.json +7 -4
- package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
- package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
- package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
- package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
- package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
- package/dist/assets/index-1CYp3zUp.js +0 -1455
- package/dist/docs-raw/CLI.md +0 -865
- package/dist-skill/docs/API/API.md +0 -1666
- package/dist-skill/docs/API/README.md +0 -37
- package/dist-skill/docs/API/assembly/assembly.md +0 -617
- package/dist-skill/docs/API/core/edge-queries.md +0 -130
- package/dist-skill/docs/API/core/parameters.md +0 -122
- package/dist-skill/docs/API/core/reserved-terms.md +0 -137
- package/dist-skill/docs/API/core/sdf.md +0 -326
- package/dist-skill/docs/API/core/skill-cli.md +0 -194
- package/dist-skill/docs/API/core/skill-guide.md +0 -205
- package/dist-skill/docs/API/core/specs.md +0 -186
- package/dist-skill/docs/API/core/topology.md +0 -372
- package/dist-skill/docs/API/entities.md +0 -268
- package/dist-skill/docs/API/output/bom.md +0 -58
- package/dist-skill/docs/API/output/brep-export.md +0 -87
- package/dist-skill/docs/API/output/dimensions.md +0 -67
- package/dist-skill/docs/API/output/export.md +0 -110
- package/dist-skill/docs/API/output/gcode.md +0 -195
- package/dist-skill/docs/API/runtime/viewport.md +0 -420
- package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
- package/dist-skill/docs/API/sketch/anchor.md +0 -37
- package/dist-skill/docs/API/sketch/booleans.md +0 -91
- package/dist-skill/docs/API/sketch/core.md +0 -73
- package/dist-skill/docs/API/sketch/extrude.md +0 -62
- package/dist-skill/docs/API/sketch/on-face.md +0 -104
- package/dist-skill/docs/API/sketch/operations.md +0 -78
- package/dist-skill/docs/API/sketch/path.md +0 -75
- package/dist-skill/docs/API/sketch/primitives.md +0 -146
- package/dist-skill/docs/API/sketch/regions.md +0 -80
- package/dist-skill/docs/API/sketch/text.md +0 -108
- package/dist-skill/docs/API/sketch/transforms.md +0 -65
- package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
- package/dist-skill/docs/CLI.md +0 -865
- package/dist-skill/docs/INDEX.md +0 -94
- package/dist-skill/docs/RELEASING.md +0 -55
- package/dist-skill/docs/cli-monetization.md +0 -111
- package/dist-skill/docs/deployment.md +0 -281
- package/dist-skill/docs/generated/concepts.md +0 -2112
- package/dist-skill/docs/internals/shape-from-slices.md +0 -152
- package/dist-skill/docs/platform/admin.md +0 -45
- package/dist-skill/docs/platform/architecture.md +0 -79
- package/dist-skill/docs/platform/auth.md +0 -110
- package/dist-skill/docs/platform/email.md +0 -67
- package/dist-skill/docs/platform/projects.md +0 -111
- package/dist-skill/docs/platform/sharing.md +0 -90
- package/dist-skill/docs/runbook.md +0 -345
package/README.md
CHANGED
|
@@ -107,14 +107,12 @@ When an AI model is asked to generate ForgeCAD models, require this workflow:
|
|
|
107
107
|
1. Read `docs/permanent/API/model-building/README.md` first.
|
|
108
108
|
2. Read every file listed there.
|
|
109
109
|
3. Read the relevant files in `examples/api/` next.
|
|
110
|
-
4.
|
|
111
|
-
5. Only then stabilize the result as `.forge.js`, or keep using the notebook when iteration is still active.
|
|
112
|
-
6. Read `docs/permanent/API/runtime/` or `docs/permanent/API/output/` only if the task explicitly needs viewport behavior, reporting, or export.
|
|
110
|
+
4. Read `docs/permanent/API/runtime/` or `docs/permanent/API/output/` only if the task explicitly needs viewport behavior, reporting, or export.
|
|
113
111
|
|
|
114
112
|
Use this instruction in prompts to avoid missing API capabilities or producing invalid model code:
|
|
115
113
|
|
|
116
114
|
```text
|
|
117
|
-
Before generating any ForgeCAD model code, read docs/permanent/API/model-building/README.md, then every file it lists, then the relevant files in examples/api/.
|
|
115
|
+
Before generating any ForgeCAD model code, read docs/permanent/API/model-building/README.md, then every file it lists, then the relevant files in examples/api/. Only read docs/permanent/API/runtime/ or docs/permanent/API/output/ if the task explicitly needs those areas. Then generate a runnable model using only documented ForgeCAD APIs and patterns from those files.
|
|
118
116
|
```
|
|
119
117
|
|
|
120
118
|
Example AI workflows:
|
|
@@ -266,12 +264,8 @@ All CLI tools use the same runtime as the browser (`src/forge/headless.ts`), so
|
|
|
266
264
|
| Task | Command |
|
|
267
265
|
| --- | --- |
|
|
268
266
|
| Validate a script | `forgecad run examples/cup.forge.js` |
|
|
269
|
-
| Validate a notebook preview | `forgecad run examples/api/notebook-iteration.forge-notebook.json` |
|
|
270
|
-
| Inspect notebook cells in the terminal | `forgecad notebook view examples/api/notebook-iteration.forge-notebook.json preview` |
|
|
271
267
|
| Render PNG views | `forgecad render examples/cup.forge.js` |
|
|
272
|
-
| Render a notebook preview | `forgecad render examples/api/notebook-iteration.forge-notebook.json` |
|
|
273
268
|
| Render orbit GIF (solid + wireframe) | `forgecad capture gif examples/cup.forge.js` |
|
|
274
|
-
| List notebook capture options | `forgecad capture gif examples/api/notebook-assembly-debug.forge-notebook.json --list` |
|
|
275
269
|
| Export sketch SVG | `forgecad export svg examples/constraints/01-fully-constrained-rect.forge.js` |
|
|
276
270
|
| Export exact STEP (supported subset only) | `forgecad export step examples/api/brep-exportable.forge.js` |
|
|
277
271
|
| Export exact BREP (supported subset only) | `forgecad export brep examples/api/brep-exportable.forge.js` |
|
|
@@ -286,7 +280,6 @@ All CLI tools use the same runtime as the browser (`src/forge/headless.ts`), so
|
|
|
286
280
|
### CLI details
|
|
287
281
|
|
|
288
282
|
- `render` outputs multi-angle PNGs (`front`, `side`, `top`, `iso`) by default.
|
|
289
|
-
- For `forgecad run`, `forgecad render`, `forgecad capture gif`, and `forgecad capture mp4`, passing a `.forge-notebook.json` uses that notebook's preview cell.
|
|
290
283
|
- `capture gif` outputs a single orbit animation with a full solid pass, then full wireframe pass.
|
|
291
284
|
- `export svg` runs fully in Node (no browser/Puppeteer).
|
|
292
285
|
- `export report` generates searchable-text PDF pages (overview, unique components, BOM, dimensions).
|
|
@@ -306,8 +299,6 @@ All CLI tools use the same runtime as the browser (`src/forge/headless.ts`), so
|
|
|
306
299
|
- `examples/api/exploded-view.forge.js`: exploded layouts + cut-plane visualization
|
|
307
300
|
- `examples/api/brep-exportable.forge.js`: exact-exportable STEP/BREP subset demo
|
|
308
301
|
- `examples/api/geometry-info.forge.js`: inspect backend/provenance info for solids
|
|
309
|
-
- `examples/api/notebook-iteration.forge-notebook.json`: stateful part exploration with pinned intermediate geometry
|
|
310
|
-
- `examples/api/notebook-assembly-debug.forge-notebook.json`: assembly collision and sweep investigation in notebook cells
|
|
311
302
|
|
|
312
303
|
BREP export support is intentionally tracked as a living parity table in [docs/permanent/API/output/brep-export.md](docs/permanent/API/output/brep-export.md).
|
|
313
304
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as reactExports, j as jsxRuntimeExports, L as Link } from "./vendor-react-
|
|
2
|
-
import {
|
|
1
|
+
import { r as reactExports, j as jsxRuntimeExports, L as Link } from "./vendor-react-CG3i_wp0.js";
|
|
2
|
+
import { a as useAuthStore } from "./index-wTEK39at.js";
|
|
3
3
|
function formatUptime(seconds) {
|
|
4
4
|
const d = Math.floor(seconds / 86400);
|
|
5
5
|
const h = Math.floor(seconds % 86400 / 3600);
|
|
@@ -12,7 +12,7 @@ function formatBytes(bytes) {
|
|
|
12
12
|
if (bytes === 0) return "0 B";
|
|
13
13
|
const units = ["B", "KB", "MB", "GB"];
|
|
14
14
|
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
15
|
-
const value = bytes /
|
|
15
|
+
const value = bytes / 1024 ** i;
|
|
16
16
|
return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
17
17
|
}
|
|
18
18
|
function timeAgo(dateStr) {
|
|
@@ -71,14 +71,20 @@ function HealthCard({ health }) {
|
|
|
71
71
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
72
72
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
|
|
73
73
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: dimTextStyle, children: "Status" }),
|
|
74
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
75
|
+
"span",
|
|
76
|
+
{
|
|
77
|
+
style: {
|
|
78
|
+
fontSize: 13,
|
|
79
|
+
fontWeight: 600,
|
|
80
|
+
padding: "2px 10px",
|
|
81
|
+
borderRadius: 6,
|
|
82
|
+
background: isOk ? "rgba(74, 222, 128, 0.12)" : "rgba(248, 113, 113, 0.12)",
|
|
83
|
+
color: isOk ? "#4ade80" : "#f87171"
|
|
84
|
+
},
|
|
85
|
+
children: isOk ? "Healthy" : health.status
|
|
86
|
+
}
|
|
87
|
+
)
|
|
82
88
|
] }),
|
|
83
89
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Row, { label: "Uptime", value: formatUptime(health.uptime) }),
|
|
84
90
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Row, { label: "Version", value: health.version })
|
|
@@ -92,17 +98,23 @@ function Row({ label, value }) {
|
|
|
92
98
|
] });
|
|
93
99
|
}
|
|
94
100
|
function Badge({ text, color }) {
|
|
95
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
102
|
+
"span",
|
|
103
|
+
{
|
|
104
|
+
style: {
|
|
105
|
+
padding: "2px 8px",
|
|
106
|
+
borderRadius: 4,
|
|
107
|
+
fontSize: 11,
|
|
108
|
+
fontWeight: 600,
|
|
109
|
+
background: `${color}18`,
|
|
110
|
+
color,
|
|
111
|
+
border: `1px solid ${color}40`,
|
|
112
|
+
textTransform: "uppercase",
|
|
113
|
+
letterSpacing: "0.03em"
|
|
114
|
+
},
|
|
115
|
+
children: text
|
|
116
|
+
}
|
|
117
|
+
);
|
|
106
118
|
}
|
|
107
119
|
function SignupSparkline({ data }) {
|
|
108
120
|
if (!data.length) return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: dimTextStyle, children: "No data" });
|
|
@@ -154,45 +166,91 @@ function ProjectBreakdown({ stats }) {
|
|
|
154
166
|
] })
|
|
155
167
|
] });
|
|
156
168
|
}
|
|
157
|
-
function
|
|
169
|
+
function PlanToggle({ userId, currentPlan, onChanged }) {
|
|
170
|
+
const [loading, setLoading] = reactExports.useState(false);
|
|
171
|
+
const isPro = currentPlan === "pro";
|
|
172
|
+
const toggle = async () => {
|
|
173
|
+
const newPlan = isPro ? "free" : "pro";
|
|
174
|
+
const label = isPro ? "Downgrade to Free" : "Grant Pro";
|
|
175
|
+
if (!confirm(`${label} for this user?`)) return;
|
|
176
|
+
setLoading(true);
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch(`/api/admin/users/${userId}/plan`, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
credentials: "include",
|
|
181
|
+
headers: { "Content-Type": "application/json" },
|
|
182
|
+
body: JSON.stringify({ plan: newPlan })
|
|
183
|
+
});
|
|
184
|
+
if (res.ok) {
|
|
185
|
+
onChanged(userId, newPlan);
|
|
186
|
+
}
|
|
187
|
+
} finally {
|
|
188
|
+
setLoading(false);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
192
|
+
"button",
|
|
193
|
+
{
|
|
194
|
+
onClick: toggle,
|
|
195
|
+
disabled: loading,
|
|
196
|
+
title: isPro ? "Click to downgrade to Free" : "Click to grant Pro",
|
|
197
|
+
style: {
|
|
198
|
+
cursor: loading ? "wait" : "pointer",
|
|
199
|
+
background: "none",
|
|
200
|
+
border: "none",
|
|
201
|
+
padding: 0,
|
|
202
|
+
opacity: loading ? 0.5 : 1
|
|
203
|
+
},
|
|
204
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { text: currentPlan, color: isPro ? "#a78bfa" : "#64748b" })
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
function UserTable({ users, total, onPlanChanged }) {
|
|
158
209
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { ...cardStyle, padding: 0, overflow: "hidden" }, children: [
|
|
159
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
210
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
211
|
+
"div",
|
|
212
|
+
{
|
|
213
|
+
style: {
|
|
214
|
+
padding: "16px 20px",
|
|
215
|
+
borderBottom: "1px solid var(--fc-border)",
|
|
216
|
+
display: "flex",
|
|
217
|
+
justifyContent: "space-between",
|
|
218
|
+
alignItems: "center"
|
|
219
|
+
},
|
|
220
|
+
children: [
|
|
221
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { style: { fontSize: 15, fontWeight: 600, color: "var(--fc-text)", margin: 0 }, children: "Users" }),
|
|
222
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { fontSize: 12, color: "var(--fc-textDim)" }, children: [
|
|
223
|
+
total,
|
|
224
|
+
" total"
|
|
225
|
+
] })
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
),
|
|
166
229
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { style: { width: "100%", borderCollapse: "collapse", minWidth: 700 }, children: [
|
|
167
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { style: { borderBottom: "1px solid var(--fc-border)" }, children: ["Name", "Email", "Role", "Plan", "Projects", "Storage", "Joined"].map((h) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
230
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { style: { borderBottom: "1px solid var(--fc-border)" }, children: ["Name", "Email", "Role", "Plan", "Projects", "Storage", "Joined"].map((h) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
231
|
+
"th",
|
|
232
|
+
{
|
|
233
|
+
style: {
|
|
234
|
+
padding: "10px 16px",
|
|
235
|
+
textAlign: "left",
|
|
236
|
+
fontSize: 11,
|
|
237
|
+
color: "var(--fc-textDim)",
|
|
238
|
+
fontWeight: 600,
|
|
239
|
+
textTransform: "uppercase",
|
|
240
|
+
whiteSpace: "nowrap"
|
|
241
|
+
},
|
|
242
|
+
children: h
|
|
243
|
+
},
|
|
244
|
+
h
|
|
245
|
+
)) }) }),
|
|
176
246
|
/* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: users.map((user) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { style: { borderBottom: "1px solid var(--fc-border)" }, children: [
|
|
177
247
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("td", { style: { padding: "10px 16px", fontSize: 13, color: "var(--fc-text)", fontWeight: 500 }, children: [
|
|
178
248
|
user.name,
|
|
179
249
|
!user.emailVerifiedAt && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: "Email not verified", style: { color: "#fb923c", marginLeft: 6, fontSize: 11 }, children: "unverified" })
|
|
180
250
|
] }),
|
|
181
251
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px", fontSize: 13, color: "var(--fc-textMuted)" }, children: user.email }),
|
|
182
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
text: user.role,
|
|
186
|
-
color: user.role === "admin" ? "#4ade80" : "#64748b"
|
|
187
|
-
}
|
|
188
|
-
) }),
|
|
189
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
190
|
-
Badge,
|
|
191
|
-
{
|
|
192
|
-
text: user.plan,
|
|
193
|
-
color: user.plan === "pro" ? "#a78bfa" : "#64748b"
|
|
194
|
-
}
|
|
195
|
-
) }),
|
|
252
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { text: user.role, color: user.role === "admin" ? "#4ade80" : "#64748b" }) }),
|
|
253
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlanToggle, { userId: user.id, currentPlan: user.plan, onChanged: onPlanChanged }) }),
|
|
196
254
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px", fontSize: 13, color: "var(--fc-text)" }, children: user.projectCount }),
|
|
197
255
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px", fontSize: 13, color: "var(--fc-textMuted)" }, children: formatBytes(user.storageUsedBytes) }),
|
|
198
256
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { style: { padding: "10px 16px", fontSize: 12, color: "var(--fc-textDim)", whiteSpace: "nowrap" }, children: new Date(user.createdAt).toLocaleDateString() })
|
|
@@ -210,38 +268,69 @@ const ACTION_COLORS = {
|
|
|
210
268
|
"share.publish": "#34d399",
|
|
211
269
|
"share.delete": "#f87171",
|
|
212
270
|
"member.add": "#818cf8",
|
|
213
|
-
"member.remove": "#fb923c"
|
|
271
|
+
"member.remove": "#fb923c",
|
|
272
|
+
"license.generate": "#60a5fa",
|
|
273
|
+
"subscription.created": "#4ade80",
|
|
274
|
+
"subscription.canceled": "#fb923c",
|
|
275
|
+
"subscription.expired": "#f87171",
|
|
276
|
+
"subscription.admin_granted": "#a78bfa",
|
|
277
|
+
"subscription.admin_revoked": "#f87171"
|
|
214
278
|
};
|
|
215
279
|
function AuditLog({ entries, onLoadMore }) {
|
|
216
280
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { ...cardStyle, padding: 0, overflow: "hidden" }, children: [
|
|
217
281
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { padding: "16px 20px", borderBottom: "1px solid var(--fc-border)" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { style: { fontSize: 15, fontWeight: 600, color: "var(--fc-text)", margin: 0 }, children: "Recent Activity" }) }),
|
|
218
282
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { maxHeight: 400, overflowY: "auto" }, children: [
|
|
219
|
-
entries.map((entry) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
283
|
+
entries.map((entry) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
style: {
|
|
287
|
+
padding: "10px 20px",
|
|
288
|
+
borderBottom: "1px solid var(--fc-border)",
|
|
289
|
+
display: "flex",
|
|
290
|
+
alignItems: "center",
|
|
291
|
+
gap: 12
|
|
292
|
+
},
|
|
293
|
+
children: [
|
|
294
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
295
|
+
"div",
|
|
296
|
+
{
|
|
297
|
+
style: {
|
|
298
|
+
width: 6,
|
|
299
|
+
height: 6,
|
|
300
|
+
borderRadius: "50%",
|
|
301
|
+
background: ACTION_COLORS[entry.action] ?? "var(--fc-textDim)",
|
|
302
|
+
flexShrink: 0
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
),
|
|
306
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
307
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
308
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 12, fontWeight: 600, color: "var(--fc-text)", fontFamily: "monospace" }, children: entry.action }),
|
|
309
|
+
entry.userName && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { fontSize: 12, color: "var(--fc-textMuted)" }, children: [
|
|
310
|
+
"by ",
|
|
311
|
+
entry.userName
|
|
312
|
+
] })
|
|
313
|
+
] }),
|
|
314
|
+
entry.details && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
315
|
+
"div",
|
|
316
|
+
{
|
|
317
|
+
style: {
|
|
318
|
+
fontSize: 11,
|
|
319
|
+
color: "var(--fc-textDim)",
|
|
320
|
+
marginTop: 2,
|
|
321
|
+
overflow: "hidden",
|
|
322
|
+
textOverflow: "ellipsis",
|
|
323
|
+
whiteSpace: "nowrap"
|
|
324
|
+
},
|
|
325
|
+
children: Object.entries(entry.details).map(([k, v]) => `${k}: ${v}`).join(", ")
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 11, color: "var(--fc-textDim)", whiteSpace: "nowrap", flexShrink: 0 }, children: timeAgo(entry.createdAt) })
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
entry.id
|
|
333
|
+
)),
|
|
245
334
|
entries.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { padding: 24, textAlign: "center", color: "var(--fc-textDim)", fontSize: 14 }, children: "No activity logged yet" })
|
|
246
335
|
] }),
|
|
247
336
|
entries.length > 0 && entries.length % 50 === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { padding: "12px 20px", borderTop: "1px solid var(--fc-border)", textAlign: "center" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -296,67 +385,89 @@ function AdminPage() {
|
|
|
296
385
|
if (d) setAudit((prev) => [...prev, ...d.entries]);
|
|
297
386
|
});
|
|
298
387
|
}, [audit.length]);
|
|
388
|
+
const handlePlanChanged = reactExports.useCallback((userId, newPlan) => {
|
|
389
|
+
setUsers((prev) => prev.map((u) => u.id === userId ? { ...u, plan: newPlan } : u));
|
|
390
|
+
}, []);
|
|
299
391
|
if (!user || user.role !== "admin") {
|
|
300
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
392
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
393
|
+
"div",
|
|
394
|
+
{
|
|
395
|
+
style: {
|
|
396
|
+
display: "flex",
|
|
397
|
+
alignItems: "center",
|
|
398
|
+
justifyContent: "center",
|
|
399
|
+
height: "100vh",
|
|
400
|
+
flexDirection: "column",
|
|
401
|
+
gap: 16
|
|
402
|
+
},
|
|
403
|
+
children: [
|
|
404
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: 48 }, children: "🚫" }),
|
|
405
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { style: { color: "var(--fc-text)", margin: 0 }, children: "Admin Access Required" }),
|
|
406
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { style: { color: "var(--fc-textMuted)", margin: 0 }, children: "You don't have permission to access this page." }),
|
|
407
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Link, { to: "/", style: { color: "var(--fc-accent)", textDecoration: "none" }, children: "Go home" })
|
|
408
|
+
]
|
|
409
|
+
}
|
|
410
|
+
);
|
|
313
411
|
}
|
|
314
412
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { minHeight: "100vh", display: "flex", flexDirection: "column", background: "var(--fc-bg)" }, children: [
|
|
315
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
padding: "0 24px",
|
|
321
|
-
background: "var(--fc-bg)",
|
|
322
|
-
borderBottom: "1px solid var(--fc-border)",
|
|
323
|
-
flexShrink: 0
|
|
324
|
-
}, children: [
|
|
325
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
326
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: "/", style: {
|
|
413
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
414
|
+
"nav",
|
|
415
|
+
{
|
|
416
|
+
style: {
|
|
417
|
+
height: 56,
|
|
327
418
|
display: "flex",
|
|
328
419
|
alignItems: "center",
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
420
|
+
justifyContent: "space-between",
|
|
421
|
+
padding: "0 24px",
|
|
422
|
+
background: "var(--fc-bg)",
|
|
423
|
+
borderBottom: "1px solid var(--fc-border)",
|
|
424
|
+
flexShrink: 0
|
|
425
|
+
},
|
|
426
|
+
children: [
|
|
427
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
428
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
429
|
+
Link,
|
|
430
|
+
{
|
|
431
|
+
to: "/",
|
|
432
|
+
style: {
|
|
433
|
+
display: "flex",
|
|
434
|
+
alignItems: "center",
|
|
435
|
+
gap: 8,
|
|
436
|
+
textDecoration: "none",
|
|
437
|
+
color: "var(--fc-text)"
|
|
438
|
+
},
|
|
439
|
+
children: [
|
|
440
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 20 }, children: "⚒" }),
|
|
441
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 16, fontWeight: 700, color: "var(--fc-accent)" }, children: "ForgeCAD" })
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
),
|
|
445
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { text: "Admin", color: "#fb923c" })
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 16 }, children: [
|
|
448
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
449
|
+
"button",
|
|
450
|
+
{
|
|
451
|
+
onClick: loadData,
|
|
452
|
+
title: `Last refresh: ${lastRefresh.toLocaleTimeString()}`,
|
|
453
|
+
style: {
|
|
454
|
+
background: "none",
|
|
455
|
+
border: "1px solid var(--fc-border)",
|
|
456
|
+
color: "var(--fc-textMuted)",
|
|
457
|
+
padding: "4px 12px",
|
|
458
|
+
borderRadius: 6,
|
|
459
|
+
cursor: "pointer",
|
|
460
|
+
fontSize: 12
|
|
461
|
+
},
|
|
462
|
+
children: "Refresh"
|
|
463
|
+
}
|
|
464
|
+
),
|
|
465
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Link, { to: "/app", style: { color: "var(--fc-textMuted)", textDecoration: "none", fontSize: 14 }, children: "Back to App" }),
|
|
466
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 13, color: "var(--fc-textDim)" }, children: user.name })
|
|
467
|
+
] })
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
),
|
|
360
471
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("main", { style: { flex: 1, padding: 24, maxWidth: 1200, margin: "0 auto", width: "100%" }, children: [
|
|
361
472
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24 }, children: [
|
|
362
473
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h1", { style: { fontSize: 28, fontWeight: 700, color: "var(--fc-text)", margin: 0 }, children: "Dashboard" }),
|
|
@@ -372,13 +483,7 @@ function AdminPage() {
|
|
|
372
483
|
sub: stats ? `+${stats.users.signupsToday} today` : void 0
|
|
373
484
|
}
|
|
374
485
|
),
|
|
375
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
376
|
-
StatNumber,
|
|
377
|
-
{
|
|
378
|
-
value: (stats == null ? void 0 : stats.projects.total) ?? "-",
|
|
379
|
-
label: "Total Projects"
|
|
380
|
-
}
|
|
381
|
-
),
|
|
486
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(StatNumber, { value: (stats == null ? void 0 : stats.projects.total) ?? "-", label: "Total Projects" }),
|
|
382
487
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
383
488
|
StatNumber,
|
|
384
489
|
{
|
|
@@ -387,20 +492,14 @@ function AdminPage() {
|
|
|
387
492
|
sub: stats ? `${stats.users.activeWeek} this week` : void 0
|
|
388
493
|
}
|
|
389
494
|
),
|
|
390
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
391
|
-
StatNumber,
|
|
392
|
-
{
|
|
393
|
-
value: stats ? formatBytes(stats.storage.totalBytes) : "-",
|
|
394
|
-
label: "Storage Used"
|
|
395
|
-
}
|
|
396
|
-
)
|
|
495
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(StatNumber, { value: stats ? formatBytes(stats.storage.totalBytes) : "-", label: "Storage Used" })
|
|
397
496
|
] }),
|
|
398
497
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 16, marginBottom: 20 }, children: [
|
|
399
498
|
/* @__PURE__ */ jsxRuntimeExports.jsx(SignupSparkline, { data: signups }),
|
|
400
499
|
stats && /* @__PURE__ */ jsxRuntimeExports.jsx(UserBreakdown, { stats }),
|
|
401
500
|
stats && /* @__PURE__ */ jsxRuntimeExports.jsx(ProjectBreakdown, { stats })
|
|
402
501
|
] }),
|
|
403
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 20 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserTable, { users, total: usersTotal }) }),
|
|
502
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 20 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserTable, { users, total: usersTotal, onPlanChanged: handlePlanChanged }) }),
|
|
404
503
|
/* @__PURE__ */ jsxRuntimeExports.jsx(AuditLog, { entries: audit, onLoadMore: loadMoreAudit })
|
|
405
504
|
] })
|
|
406
505
|
] });
|