cronus-ui 0.6.8 → 0.6.11
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 -2
- package/dist/compose/gold-path.d.ts +1 -1
- package/dist/compose/gold-path.js +84 -18
- package/dist/config.d.ts +2 -2
- package/dist/config.js +1 -1
- package/package.json +2 -2
- package/templates/apps/admin.json +8 -2
- package/templates/apps/saas.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ Notes that match the commander surface:
|
|
|
49
49
|
`packages/cli/scripts/build-registry.ts` — each item carries its source, its npm
|
|
50
50
|
`dependencies`, and its `registryDependencies` (other components it imports),
|
|
51
51
|
derived by parsing imports.
|
|
52
|
-
- The default registry is pinned to the CLI package version (`v0.6.
|
|
52
|
+
- The default registry is pinned to the CLI package version (`v0.6.11` here), not
|
|
53
53
|
mutable `main`, so a published CLI reads the registry snapshot it was released
|
|
54
54
|
with. Use `-r, --registry ./registry` when testing local registry changes before
|
|
55
55
|
a release tag exists.
|
|
@@ -80,7 +80,7 @@ Notes that match the commander surface:
|
|
|
80
80
|
"lib": "lib",
|
|
81
81
|
"blocks": "components/blocks"
|
|
82
82
|
},
|
|
83
|
-
"registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.
|
|
83
|
+
"registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.11/registry"
|
|
84
84
|
}
|
|
85
85
|
```
|
|
86
86
|
|
|
@@ -35,7 +35,7 @@ export declare function goldPathLayout(config: CronusUIConfig): GoldPathLayout;
|
|
|
35
35
|
* anchors are missing and nothing else can be patched.
|
|
36
36
|
*/
|
|
37
37
|
export declare function patchChromeSource(source: string, workspaceImport: string, inviteImport: string, sessionImport?: string): string | undefined;
|
|
38
|
-
/** Insert ItemsPanel
|
|
38
|
+
/** Insert ItemsPanel and drop catalog dashboard/stats from a generated home. */
|
|
39
39
|
export declare function patchHomePageSource(source: string, itemsImport: string): string | undefined;
|
|
40
40
|
/**
|
|
41
41
|
* Replace the catalog TeamBlock on a generated /team page with MembersPanel.
|
|
@@ -629,7 +629,7 @@ function membersActionsSource(authImport) {
|
|
|
629
629
|
import { and, asc, eq } from "drizzle-orm";
|
|
630
630
|
import { headers } from "next/headers";
|
|
631
631
|
import { db } from "@/db";
|
|
632
|
-
import { member, organization, user } from "@/db/schema";
|
|
632
|
+
import { invitation, member, organization, user } from "@/db/schema";
|
|
633
633
|
import { auth } from ${JSON.stringify(authImport)};
|
|
634
634
|
|
|
635
635
|
async function activeWorkspaceId(): Promise<string | null> {
|
|
@@ -656,9 +656,10 @@ async function activeWorkspaceId(): Promise<string | null> {
|
|
|
656
656
|
export async function loadMembers(): Promise<{
|
|
657
657
|
workspace: string;
|
|
658
658
|
rows: { id: string; name: string; email: string; image: string | null; role: string }[];
|
|
659
|
+
pending: { id: string; email: string; role: string | null }[];
|
|
659
660
|
}> {
|
|
660
661
|
const orgId = await activeWorkspaceId();
|
|
661
|
-
if (!orgId) return { workspace: "no workspace", rows: [] };
|
|
662
|
+
if (!orgId) return { workspace: "no workspace", rows: [], pending: [] };
|
|
662
663
|
const [org] = await db.select().from(organization).where(eq(organization.id, orgId)).limit(1);
|
|
663
664
|
const rows = await db
|
|
664
665
|
.select({
|
|
@@ -672,7 +673,16 @@ export async function loadMembers(): Promise<{
|
|
|
672
673
|
.innerJoin(user, eq(member.userId, user.id))
|
|
673
674
|
.where(eq(member.organizationId, orgId))
|
|
674
675
|
.orderBy(asc(member.createdAt));
|
|
675
|
-
|
|
676
|
+
const pending = await db
|
|
677
|
+
.select({
|
|
678
|
+
id: invitation.id,
|
|
679
|
+
email: invitation.email,
|
|
680
|
+
role: invitation.role,
|
|
681
|
+
})
|
|
682
|
+
.from(invitation)
|
|
683
|
+
.where(and(eq(invitation.organizationId, orgId), eq(invitation.status, "pending")))
|
|
684
|
+
.orderBy(asc(invitation.createdAt));
|
|
685
|
+
return { workspace: org?.name ?? "no workspace", rows, pending };
|
|
676
686
|
}
|
|
677
687
|
`;
|
|
678
688
|
}
|
|
@@ -682,15 +692,16 @@ import { MembersView } from ${JSON.stringify(viewImport)};
|
|
|
682
692
|
|
|
683
693
|
export async function MembersPanel() {
|
|
684
694
|
const data = await loadMembers();
|
|
685
|
-
return <MembersView workspace={data.workspace} rows={data.rows} />;
|
|
695
|
+
return <MembersView workspace={data.workspace} rows={data.rows} pending={data.pending} />;
|
|
686
696
|
}
|
|
687
697
|
`;
|
|
688
698
|
}
|
|
689
699
|
function membersViewSource(inviteImport) {
|
|
690
700
|
return `"use client";
|
|
691
701
|
|
|
692
|
-
import { Avatar, AvatarFallback, AvatarImage, Badge, Button } from "@cronus-ui/ui";
|
|
702
|
+
import { Avatar, AvatarFallback, AvatarImage, Badge, Button, CopyButton } from "@cronus-ui/ui";
|
|
693
703
|
import { InviteMember } from ${JSON.stringify(inviteImport)};
|
|
704
|
+
import { useEffect, useState } from "react";
|
|
694
705
|
|
|
695
706
|
function initialsOf(name: string, email: string): string {
|
|
696
707
|
const parts = name.trim().split(/\\s+/).filter(Boolean);
|
|
@@ -714,13 +725,23 @@ function roleVariant(role: string): "primary" | "info" | "secondary" {
|
|
|
714
725
|
return "secondary";
|
|
715
726
|
}
|
|
716
727
|
|
|
728
|
+
function inviteHref(origin: string, id: string): string {
|
|
729
|
+
return \`\${origin}/accept-invitation?id=\${encodeURIComponent(id)}\`;
|
|
730
|
+
}
|
|
731
|
+
|
|
717
732
|
export function MembersView({
|
|
718
733
|
workspace,
|
|
719
734
|
rows,
|
|
735
|
+
pending,
|
|
720
736
|
}: {
|
|
721
737
|
workspace: string;
|
|
722
738
|
rows: { id: string; name: string; email: string; image: string | null; role: string }[];
|
|
739
|
+
pending: { id: string; email: string; role: string | null }[];
|
|
723
740
|
}) {
|
|
741
|
+
const [origin, setOrigin] = useState("");
|
|
742
|
+
useEffect(() => {
|
|
743
|
+
setOrigin(window.location.origin);
|
|
744
|
+
}, []);
|
|
724
745
|
const count = String(rows.length);
|
|
725
746
|
return (
|
|
726
747
|
<section
|
|
@@ -768,6 +789,33 @@ export function MembersView({
|
|
|
768
789
|
))}
|
|
769
790
|
</ul>
|
|
770
791
|
)}
|
|
792
|
+
{pending.length > 0 ? (
|
|
793
|
+
<>
|
|
794
|
+
<h2 className="mt-6 text-sm font-semibold text-fg">Pending</h2>
|
|
795
|
+
<ul>
|
|
796
|
+
{pending.map((row) => (
|
|
797
|
+
<li
|
|
798
|
+
key={row.id}
|
|
799
|
+
data-slot="pending-invite"
|
|
800
|
+
className="flex items-center gap-3 border-t border-border py-3"
|
|
801
|
+
>
|
|
802
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
803
|
+
<span className="truncate text-sm text-fg">{row.email}</span>
|
|
804
|
+
<span className="truncate text-sm text-fg-tertiary">
|
|
805
|
+
{roleLabel(row.role ?? "member")}
|
|
806
|
+
</span>
|
|
807
|
+
</div>
|
|
808
|
+
<CopyButton
|
|
809
|
+
value={inviteHref(origin, row.id)}
|
|
810
|
+
copyLabel="Copy invite link"
|
|
811
|
+
variant="outline"
|
|
812
|
+
size="icon"
|
|
813
|
+
/>
|
|
814
|
+
</li>
|
|
815
|
+
))}
|
|
816
|
+
</ul>
|
|
817
|
+
</>
|
|
818
|
+
) : null}
|
|
771
819
|
</section>
|
|
772
820
|
);
|
|
773
821
|
}
|
|
@@ -811,20 +859,33 @@ function inviteMemberSource(authClientImport) {
|
|
|
811
859
|
return `"use client";
|
|
812
860
|
|
|
813
861
|
import { InviteDialog } from "@cronus-ui/ui";
|
|
814
|
-
import
|
|
862
|
+
import { useRouter } from "next/navigation";
|
|
863
|
+
import { type ReactNode, useRef } from "react";
|
|
815
864
|
import { authClient } from ${JSON.stringify(authClientImport)};
|
|
816
865
|
|
|
817
866
|
export function InviteMember({ trigger }: { trigger: ReactNode }) {
|
|
867
|
+
const router = useRouter();
|
|
868
|
+
const sent = useRef(false);
|
|
818
869
|
return (
|
|
819
870
|
<InviteDialog
|
|
820
871
|
trigger={trigger}
|
|
872
|
+
onOpenChange={(open) => {
|
|
873
|
+
if (!open && sent.current) {
|
|
874
|
+
sent.current = false;
|
|
875
|
+
router.refresh();
|
|
876
|
+
}
|
|
877
|
+
}}
|
|
821
878
|
onInvite={async ({ email, role }) => {
|
|
822
879
|
const assigned = role === "admin" || role === "owner" ? role : "member";
|
|
823
|
-
const { error } = await authClient.organization.inviteMember({
|
|
880
|
+
const { data, error } = await authClient.organization.inviteMember({
|
|
824
881
|
email,
|
|
825
882
|
role: assigned,
|
|
826
883
|
});
|
|
827
884
|
if (error) throw new Error(error.message || "Invite failed");
|
|
885
|
+
const id = data?.id ? String(data.id) : "";
|
|
886
|
+
if (!id) return;
|
|
887
|
+
sent.current = true;
|
|
888
|
+
return { url: \`\${window.location.origin}/accept-invitation?id=\${encodeURIComponent(id)}\` };
|
|
828
889
|
}}
|
|
829
890
|
/>
|
|
830
891
|
);
|
|
@@ -1054,25 +1115,30 @@ const nextConfig = {
|
|
|
1054
1115
|
export default nextConfig;
|
|
1055
1116
|
`;
|
|
1056
1117
|
}
|
|
1057
|
-
/**
|
|
1118
|
+
/**
|
|
1119
|
+
* Catalog demo surfaces that compose stacks on `/` for saas/admin. Gold path
|
|
1120
|
+
* keeps the files installed but the live home is ItemsPanel only.
|
|
1121
|
+
*/
|
|
1122
|
+
const CATALOG_HOME_BLOCKS = [
|
|
1123
|
+
"DashboardAnalyticsBlock",
|
|
1124
|
+
"DashboardAdminOverviewBlock",
|
|
1125
|
+
"StatsBlock",
|
|
1126
|
+
"DashboardBlock",
|
|
1127
|
+
];
|
|
1128
|
+
/** Insert ItemsPanel and drop catalog dashboard/stats from a generated home. */
|
|
1058
1129
|
export function patchHomePageSource(source, itemsImport) {
|
|
1059
1130
|
if (!/<main\b/.test(source))
|
|
1060
1131
|
return undefined;
|
|
1061
1132
|
let out = source;
|
|
1062
|
-
|
|
1063
|
-
if (!out.includes(importLine)) {
|
|
1064
|
-
const match = out.match(/^import .+$/m);
|
|
1065
|
-
if (match?.index !== undefined) {
|
|
1066
|
-
out = `${out.slice(0, match.index)}${importLine}\n${out.slice(match.index)}`;
|
|
1067
|
-
}
|
|
1068
|
-
else {
|
|
1069
|
-
out = `${importLine}\n${out}`;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1133
|
+
out = insertImport(out, `import { ItemsPanel } from ${JSON.stringify(itemsImport)};`);
|
|
1072
1134
|
out = out.replace(/export default(?! async) function/, "export default async function");
|
|
1073
1135
|
if (!/<ItemsPanel\s*\/>/.test(out)) {
|
|
1074
1136
|
out = out.replace(/(<main\b[^>]*>)/, "$1\n <ItemsPanel />");
|
|
1075
1137
|
}
|
|
1138
|
+
for (const name of CATALOG_HOME_BLOCKS) {
|
|
1139
|
+
out = out.replace(new RegExp(`\\n\\s*<${name}\\s*/>`, "g"), "");
|
|
1140
|
+
out = out.replace(new RegExp(`import \\{ ${name} \\} from "[^"]+";\\n`), "");
|
|
1141
|
+
}
|
|
1076
1142
|
return out;
|
|
1077
1143
|
}
|
|
1078
1144
|
/**
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const CONFIG_FILE = "cronus-ui.json";
|
|
2
|
-
export declare const CLI_VERSION = "0.6.
|
|
3
|
-
export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.
|
|
2
|
+
export declare const CLI_VERSION = "0.6.11";
|
|
3
|
+
export declare const DEFAULT_REGISTRY = "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.11/registry";
|
|
4
4
|
/** Manifest entry `add`/`upgrade` record per installed registry item. */
|
|
5
5
|
export interface InstalledRecord {
|
|
6
6
|
/** Registry release the files came from (git tag without the leading "v"). */
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
export const CONFIG_FILE = "cronus-ui.json";
|
|
5
|
-
export const CLI_VERSION = "0.6.
|
|
5
|
+
export const CLI_VERSION = "0.6.11";
|
|
6
6
|
export const DEFAULT_REGISTRY = `https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v${CLI_VERSION}/registry`;
|
|
7
7
|
export const DEFAULT_CONFIG = {
|
|
8
8
|
aliases: { ui: "@/components/ui", lib: "@/lib", blocks: "@/components/blocks" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cronus-ui",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "cronus-ui — add Cronus UI components to your project, shadcn-style (copy-paste registry).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"registry:check": "bun run scripts/check-registry.ts"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@cronus-ui/ai-kit": "0.6.
|
|
57
|
+
"@cronus-ui/ai-kit": "0.6.11",
|
|
58
58
|
"commander": "^15.0.0",
|
|
59
59
|
"picocolors": "^1.1.1"
|
|
60
60
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"planVersion": 1,
|
|
5
5
|
"manifest": {
|
|
6
6
|
"title": "Admin",
|
|
7
|
-
"description": "An admin console: split login, an (app) shell, overview, users, analytics, a sprint board and an audit trail — composed from validated blocks.",
|
|
7
|
+
"description": "An admin console: split login/signup, password reset, an (app) shell, overview, users, analytics, a sprint board and an audit trail — composed from validated blocks.",
|
|
8
8
|
"chrome": {
|
|
9
9
|
"shell": { "block": "app-shell-chrome" },
|
|
10
10
|
"bare": {}
|
|
@@ -16,11 +16,17 @@
|
|
|
16
16
|
"chrome": "bare",
|
|
17
17
|
"blocks": [{ "block": "login", "variant": "split" }]
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
"route": "/signup",
|
|
21
|
+
"title": "Create account",
|
|
22
|
+
"chrome": "bare",
|
|
23
|
+
"blocks": [{ "block": "signup", "variant": "split" }]
|
|
24
|
+
},
|
|
19
25
|
{
|
|
20
26
|
"route": "/forgot-password",
|
|
21
27
|
"title": "Reset password",
|
|
22
28
|
"chrome": "bare",
|
|
23
|
-
"blocks": ["forgot-password"]
|
|
29
|
+
"blocks": [{ "block": "forgot-password", "variant": "split" }]
|
|
24
30
|
},
|
|
25
31
|
{
|
|
26
32
|
"route": "/reset-password",
|
package/templates/apps/saas.json
CHANGED