@nubase/create 0.1.9 → 0.1.12

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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/templates/backend/src/api/routes/dashboard.ts +122 -0
  3. package/templates/backend/src/index.ts +3 -1
  4. package/templates/frontend/src/config.tsx +4 -0
  5. package/templates/frontend/src/dashboards/analytics.ts +67 -0
  6. package/templates/schema/src/api-endpoints.ts +25 -9
  7. package/templates/schema/src/{schema → endpoints}/auth/get-me.ts +1 -1
  8. package/templates/schema/src/{schema → endpoints}/auth/index.ts +0 -2
  9. package/templates/schema/src/{schema → endpoints}/auth/login-complete.ts +3 -3
  10. package/templates/schema/src/{schema → endpoints}/auth/login-start.ts +2 -2
  11. package/templates/schema/src/{schema → endpoints}/auth/login.ts +1 -1
  12. package/templates/schema/src/{schema → endpoints}/auth/signup.ts +3 -3
  13. package/templates/schema/src/endpoints/dashboard/active-users.ts +6 -0
  14. package/templates/schema/src/endpoints/dashboard/browser-stats.ts +6 -0
  15. package/templates/schema/src/endpoints/dashboard/index.ts +6 -0
  16. package/templates/schema/src/endpoints/dashboard/recent-activity.ts +6 -0
  17. package/templates/schema/src/endpoints/dashboard/revenue-chart.ts +6 -0
  18. package/templates/schema/src/endpoints/dashboard/sales-chart.ts +6 -0
  19. package/templates/schema/src/endpoints/dashboard/total-revenue.ts +6 -0
  20. package/templates/schema/src/endpoints/index.ts +3 -0
  21. package/templates/schema/src/{schema → endpoints}/ticket/get-ticket.ts +2 -2
  22. package/templates/schema/src/{schema → endpoints}/ticket/get-tickets.ts +3 -3
  23. package/templates/schema/src/{schema → endpoints}/ticket/index.ts +0 -1
  24. package/templates/schema/src/{schema → endpoints}/ticket/patch-ticket.ts +3 -3
  25. package/templates/schema/src/{schema → endpoints}/ticket/post-ticket.ts +3 -3
  26. package/templates/schema/src/index.ts +1 -2
  27. package/templates/schema/src/resources/index.ts +3 -0
  28. package/templates/schema/src/{schema/ticket/ticket-base.ts → resources/ticket.ts} +1 -1
  29. package/templates/schema/src/{schema/auth → resources}/workspace.ts +1 -1
  30. /package/templates/schema/src/{schema → endpoints}/auth/logout.ts +0 -0
  31. /package/templates/schema/src/{schema → endpoints}/ticket/delete-ticket.ts +0 -0
  32. /package/templates/schema/src/{schema/auth → resources}/user.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubase/create",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "Create a new Nubase application",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,122 @@
1
+ import { createHttpHandler } from "@nubase/backend";
2
+ import { apiEndpoints } from "schema";
3
+
4
+ /**
5
+ * Dashboard widget endpoints.
6
+ * These return hardcoded data for demonstration - real implementations would query the database.
7
+ */
8
+ export const dashboardHandlers = {
9
+ /** Revenue chart - returns series data for area/line/bar charts. */
10
+ getRevenueChart: createHttpHandler({
11
+ endpoint: apiEndpoints.getRevenueChart,
12
+ handler: async () => ({
13
+ type: "series",
14
+ config: {
15
+ keys: ["desktop", "mobile"],
16
+ },
17
+ data: [
18
+ { category: "January", desktop: 186, mobile: 80 },
19
+ { category: "February", desktop: 305, mobile: 200 },
20
+ { category: "March", desktop: 237, mobile: 120 },
21
+ { category: "April", desktop: 73, mobile: 190 },
22
+ { category: "May", desktop: 209, mobile: 130 },
23
+ { category: "June", desktop: 214, mobile: 140 },
24
+ ],
25
+ }),
26
+ }),
27
+
28
+ /** Browser stats - returns proportional data for pie/donut charts. */
29
+ getBrowserStats: createHttpHandler({
30
+ endpoint: apiEndpoints.getBrowserStats,
31
+ handler: async () => ({
32
+ type: "proportional",
33
+ data: [
34
+ { label: "Chrome", value: 275 },
35
+ { label: "Safari", value: 200 },
36
+ { label: "Firefox", value: 187 },
37
+ { label: "Edge", value: 173 },
38
+ { label: "Other", value: 90 },
39
+ ],
40
+ }),
41
+ }),
42
+
43
+ /** Total revenue KPI - returns single value with trend. */
44
+ getTotalRevenue: createHttpHandler({
45
+ endpoint: apiEndpoints.getTotalRevenue,
46
+ handler: async () => ({
47
+ type: "kpi",
48
+ value: "$45,231.89",
49
+ label: "Total Revenue",
50
+ trend: "+20.1% from last month",
51
+ trendDirection: "up",
52
+ }),
53
+ }),
54
+
55
+ /** Active users KPI - returns single value with trend. */
56
+ getActiveUsers: createHttpHandler({
57
+ endpoint: apiEndpoints.getActiveUsers,
58
+ handler: async () => ({
59
+ type: "kpi",
60
+ value: "+2,350",
61
+ label: "Active Users",
62
+ trend: "+180.1% from last month",
63
+ trendDirection: "up",
64
+ }),
65
+ }),
66
+
67
+ /** Sales chart - returns series data for bar charts. */
68
+ getSalesChart: createHttpHandler({
69
+ endpoint: apiEndpoints.getSalesChart,
70
+ handler: async () => ({
71
+ type: "series",
72
+ config: {
73
+ keys: ["sales"],
74
+ },
75
+ data: [
76
+ { category: "Mon", sales: 12 },
77
+ { category: "Tue", sales: 19 },
78
+ { category: "Wed", sales: 3 },
79
+ { category: "Thu", sales: 5 },
80
+ { category: "Fri", sales: 2 },
81
+ { category: "Sat", sales: 8 },
82
+ { category: "Sun", sales: 15 },
83
+ ],
84
+ }),
85
+ }),
86
+
87
+ /** Recent activity - returns table data. */
88
+ getRecentActivity: createHttpHandler({
89
+ endpoint: apiEndpoints.getRecentActivity,
90
+ handler: async () => ({
91
+ type: "table",
92
+ columns: [
93
+ { key: "user", label: "User", width: "30%" },
94
+ { key: "action", label: "Action", width: "40%" },
95
+ { key: "time", label: "Time", width: "30%" },
96
+ ],
97
+ rows: [
98
+ { user: "John Doe", action: "Created a new ticket", time: "2 min ago" },
99
+ {
100
+ user: "Jane Smith",
101
+ action: "Updated project settings",
102
+ time: "5 min ago",
103
+ },
104
+ {
105
+ user: "Bob Johnson",
106
+ action: "Closed ticket #123",
107
+ time: "10 min ago",
108
+ },
109
+ {
110
+ user: "Alice Brown",
111
+ action: "Added comment on ticket #456",
112
+ time: "15 min ago",
113
+ },
114
+ {
115
+ user: "Charlie Wilson",
116
+ action: "Assigned ticket to team",
117
+ time: "20 min ago",
118
+ },
119
+ ],
120
+ }),
121
+ }),
122
+ };
@@ -2,9 +2,10 @@ import { serve } from "@hono/node-server";
2
2
  import { createAuthMiddleware, registerHandlers } from "@nubase/backend";
3
3
  import { Hono } from "hono";
4
4
  import { cors } from "hono/cors";
5
- import { ticketHandlers } from "./api/routes/ticket";
6
5
  import { authHandlers } from "./api/routes/auth";
6
+ import { dashboardHandlers } from "./api/routes/dashboard";
7
7
  import { testUtilsHandlers } from "./api/routes/test-utils";
8
+ import { ticketHandlers } from "./api/routes/ticket";
8
9
  import {
9
10
  createPostAuthWorkspaceMiddleware,
10
11
  createWorkspaceMiddleware,
@@ -50,6 +51,7 @@ app.get("/", (c) => c.json({ message: "Welcome to __PROJECT_NAME_PASCAL__ API" }
50
51
  // Register all handlers - path and method extracted from endpoint metadata
51
52
  registerHandlers(app, authHandlers);
52
53
  registerHandlers(app, ticketHandlers);
54
+ registerHandlers(app, dashboardHandlers);
53
55
 
54
56
  // Register test utility handlers (only in test environment)
55
57
  if (process.env.NODE_ENV === "test") {
@@ -3,6 +3,7 @@ import { defaultKeybindings, resourceLink } from "@nubase/frontend";
3
3
  import { Home, TicketIcon } from "lucide-react";
4
4
  import { apiEndpoints } from "schema";
5
5
  import { __PROJECT_NAME_PASCAL__AuthController } from "./auth/__PROJECT_NAME_PASCAL__AuthController";
6
+ import { analyticsDashboard } from "./dashboards/analytics";
6
7
  import { ticketResource } from "./resources/ticket";
7
8
 
8
9
  const apiBaseUrl =
@@ -35,4 +36,7 @@ export const config: NubaseFrontendConfig<typeof apiEndpoints> = {
35
36
  defaultThemeId: "dark",
36
37
  authentication: authController,
37
38
  publicRoutes: ["/signin"],
39
+ dashboards: {
40
+ [analyticsDashboard.id]: analyticsDashboard,
41
+ },
38
42
  };
@@ -0,0 +1,67 @@
1
+ import { createDashboard } from "@nubase/frontend";
2
+ import { apiEndpoints } from "schema";
3
+
4
+ /**
5
+ * Analytics dashboard configuration.
6
+ *
7
+ * This dashboard demonstrates the type-safe widget system:
8
+ * - Each widget references an endpoint that returns the correct data type
9
+ * - TypeScript will error if you try to use a non-matching endpoint
10
+ * - Layout is defined using react-grid-layout coordinates (x, y, w, h)
11
+ */
12
+ export const analyticsDashboard = createDashboard("analytics")
13
+ .withApiEndpoints(apiEndpoints)
14
+ .withTitle("Analytics Dashboard")
15
+ .withWidgets([
16
+ // Revenue trend - area chart spanning most of the top row
17
+ {
18
+ type: "series",
19
+ id: "revenue-chart",
20
+ title: "Revenue Trend",
21
+ variant: "area",
22
+ endpoint: "getRevenueChart",
23
+ defaultLayout: { x: 0, y: 0, w: 8, h: 3 },
24
+ },
25
+ // Browser stats - donut chart on the right
26
+ {
27
+ type: "proportional",
28
+ id: "browser-stats",
29
+ title: "Browser Usage",
30
+ variant: "donut",
31
+ endpoint: "getBrowserStats",
32
+ defaultLayout: { x: 8, y: 0, w: 4, h: 3 },
33
+ },
34
+ // KPI cards - second row
35
+ {
36
+ type: "kpi",
37
+ id: "total-revenue",
38
+ title: "Total Revenue",
39
+ endpoint: "getTotalRevenue",
40
+ defaultLayout: { x: 0, y: 3, w: 3, h: 2 },
41
+ },
42
+ {
43
+ type: "kpi",
44
+ id: "active-users",
45
+ title: "Active Users",
46
+ endpoint: "getActiveUsers",
47
+ defaultLayout: { x: 3, y: 3, w: 3, h: 2 },
48
+ },
49
+ // Sales chart - bar chart
50
+ {
51
+ type: "series",
52
+ id: "sales-chart",
53
+ title: "Weekly Sales",
54
+ variant: "bar",
55
+ endpoint: "getSalesChart",
56
+ defaultLayout: { x: 6, y: 3, w: 6, h: 2 },
57
+ },
58
+ // Recent activity table - bottom row
59
+ {
60
+ type: "table",
61
+ id: "recent-activity",
62
+ title: "Recent Activity",
63
+ endpoint: "getRecentActivity",
64
+ maxRows: 5,
65
+ defaultLayout: { x: 0, y: 5, w: 12, h: 3 },
66
+ },
67
+ ]);
@@ -5,23 +5,24 @@ import {
5
5
  loginStartSchema,
6
6
  logoutSchema,
7
7
  signupSchema,
8
- } from "./schema/auth";
8
+ } from "./endpoints/auth";
9
+ import {
10
+ getActiveUsersSchema,
11
+ getBrowserStatsSchema,
12
+ getRecentActivitySchema,
13
+ getRevenueChartSchema,
14
+ getSalesChartSchema,
15
+ getTotalRevenueSchema,
16
+ } from "./endpoints/dashboard";
9
17
  import {
10
18
  deleteTicketSchema,
11
19
  getTicketSchema,
12
20
  getTicketsSchema,
13
21
  patchTicketSchema,
14
22
  postTicketSchema,
15
- } from "./schema/ticket";
23
+ } from "./endpoints/ticket";
16
24
 
17
25
  export const apiEndpoints = {
18
- // Tickets
19
- getTickets: getTicketsSchema,
20
- getTicket: getTicketSchema,
21
- postTicket: postTicketSchema,
22
- patchTicket: patchTicketSchema,
23
- deleteTicket: deleteTicketSchema,
24
-
25
26
  // Auth
26
27
  loginStart: loginStartSchema,
27
28
  loginComplete: loginCompleteSchema,
@@ -29,6 +30,21 @@ export const apiEndpoints = {
29
30
  logout: logoutSchema,
30
31
  getMe: getMeSchema,
31
32
  signup: signupSchema,
33
+
34
+ // Tickets
35
+ getTickets: getTicketsSchema,
36
+ getTicket: getTicketSchema,
37
+ postTicket: postTicketSchema,
38
+ patchTicket: patchTicketSchema,
39
+ deleteTicket: deleteTicketSchema,
40
+
41
+ // Dashboard widgets
42
+ getRevenueChart: getRevenueChartSchema,
43
+ getBrowserStats: getBrowserStatsSchema,
44
+ getTotalRevenue: getTotalRevenueSchema,
45
+ getActiveUsers: getActiveUsersSchema,
46
+ getSalesChart: getSalesChartSchema,
47
+ getRecentActivity: getRecentActivitySchema,
32
48
  } as const;
33
49
 
34
50
  export type ApiEndpoints = typeof apiEndpoints;
@@ -1,5 +1,5 @@
1
1
  import { emptySchema, nu, type RequestSchema } from "@nubase/core";
2
- import { userSchema } from "./user";
2
+ import { userSchema } from "../../resources/user";
3
3
 
4
4
  /**
5
5
  * Get current user schema
@@ -4,5 +4,3 @@ export { loginCompleteSchema } from "./login-complete";
4
4
  export { loginStartSchema } from "./login-start";
5
5
  export { logoutSchema } from "./logout";
6
6
  export { signupSchema } from "./signup";
7
- export { userSchema } from "./user";
8
- export { workspaceInfoSchema } from "./workspace";
@@ -1,6 +1,6 @@
1
1
  import { emptySchema, nu, type RequestSchema } from "@nubase/core";
2
- import { userSchema } from "./user";
3
- import { workspaceInfoSchema } from "./workspace";
2
+ import { userSchema } from "../../resources/user";
3
+ import { workspaceSchema } from "../../resources/workspace";
4
4
 
5
5
  /**
6
6
  * Login complete request schema - Step 2 of two-step auth
@@ -20,6 +20,6 @@ export const loginCompleteSchema = {
20
20
  }),
21
21
  responseBody: nu.object({
22
22
  user: userSchema,
23
- workspace: workspaceInfoSchema,
23
+ workspace: workspaceSchema,
24
24
  }),
25
25
  } satisfies RequestSchema;
@@ -1,5 +1,5 @@
1
1
  import { emptySchema, nu, type RequestSchema } from "@nubase/core";
2
- import { workspaceInfoSchema } from "./workspace";
2
+ import { workspaceSchema } from "../../resources/workspace";
3
3
 
4
4
  /**
5
5
  * Login start request schema - Step 1 of two-step auth
@@ -23,6 +23,6 @@ export const loginStartSchema = {
23
23
  /** User's email (for display) */
24
24
  email: nu.string(),
25
25
  /** List of workspaces the user belongs to */
26
- workspaces: nu.array(workspaceInfoSchema),
26
+ workspaces: nu.array(workspaceSchema),
27
27
  }),
28
28
  } satisfies RequestSchema;
@@ -1,5 +1,5 @@
1
1
  import { emptySchema, nu, type RequestSchema } from "@nubase/core";
2
- import { userSchema } from "./user";
2
+ import { userSchema } from "../../resources/user";
3
3
 
4
4
  /**
5
5
  * Legacy login request schema (kept for backwards compatibility)
@@ -1,6 +1,6 @@
1
1
  import { emptySchema, nu, type RequestSchema } from "@nubase/core";
2
- import { userSchema } from "./user";
3
- import { workspaceInfoSchema } from "./workspace";
2
+ import { userSchema } from "../../resources/user";
3
+ import { workspaceSchema } from "../../resources/workspace";
4
4
 
5
5
  /**
6
6
  * Signup request schema
@@ -27,6 +27,6 @@ export const signupSchema = {
27
27
  }),
28
28
  responseBody: nu.object({
29
29
  user: userSchema,
30
- workspace: workspaceInfoSchema,
30
+ workspace: workspaceSchema,
31
31
  }),
32
32
  } satisfies RequestSchema;
@@ -0,0 +1,6 @@
1
+ import { createKpiWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Active users KPI - returns single value with trend */
4
+ export const getActiveUsersSchema = createKpiWidgetEndpoint(
5
+ "/dashboard/active-users",
6
+ );
@@ -0,0 +1,6 @@
1
+ import { createProportionalWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Browser stats - returns proportional data for pie/donut charts */
4
+ export const getBrowserStatsSchema = createProportionalWidgetEndpoint(
5
+ "/dashboard/browser-stats",
6
+ );
@@ -0,0 +1,6 @@
1
+ export { getActiveUsersSchema } from "./active-users";
2
+ export { getBrowserStatsSchema } from "./browser-stats";
3
+ export { getRecentActivitySchema } from "./recent-activity";
4
+ export { getRevenueChartSchema } from "./revenue-chart";
5
+ export { getSalesChartSchema } from "./sales-chart";
6
+ export { getTotalRevenueSchema } from "./total-revenue";
@@ -0,0 +1,6 @@
1
+ import { createTableWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Recent activity - returns table data */
4
+ export const getRecentActivitySchema = createTableWidgetEndpoint(
5
+ "/dashboard/recent-activity",
6
+ );
@@ -0,0 +1,6 @@
1
+ import { createSeriesWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Revenue chart - returns series data for area/line/bar charts */
4
+ export const getRevenueChartSchema = createSeriesWidgetEndpoint(
5
+ "/dashboard/revenue-chart",
6
+ );
@@ -0,0 +1,6 @@
1
+ import { createSeriesWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Sales chart - returns series data for bar charts */
4
+ export const getSalesChartSchema = createSeriesWidgetEndpoint(
5
+ "/dashboard/sales-chart",
6
+ );
@@ -0,0 +1,6 @@
1
+ import { createKpiWidgetEndpoint } from "@nubase/core";
2
+
3
+ /** Total revenue KPI - returns single value with trend */
4
+ export const getTotalRevenueSchema = createKpiWidgetEndpoint(
5
+ "/dashboard/total-revenue",
6
+ );
@@ -0,0 +1,3 @@
1
+ export * from "./auth";
2
+ export * from "./dashboard";
3
+ export * from "./ticket";
@@ -1,9 +1,9 @@
1
1
  import { idNumberSchema, type RequestSchema } from "@nubase/core";
2
- import { ticketBaseSchema } from "./ticket-base";
2
+ import { ticketSchema } from "../../resources/ticket";
3
3
 
4
4
  export const getTicketSchema = {
5
5
  method: "GET" as const,
6
6
  path: "/tickets/:id",
7
7
  requestParams: idNumberSchema,
8
- responseBody: ticketBaseSchema,
8
+ responseBody: ticketSchema,
9
9
  } satisfies RequestSchema;
@@ -1,9 +1,9 @@
1
1
  import { nu, type RequestSchema } from "@nubase/core";
2
- import { ticketBaseSchema } from "./ticket-base";
2
+ import { ticketSchema } from "../../resources/ticket";
3
3
 
4
4
  export const getTicketsSchema = {
5
5
  method: "GET" as const,
6
6
  path: "/tickets",
7
- requestParams: ticketBaseSchema.omit("id").partial(),
8
- responseBody: nu.array(ticketBaseSchema),
7
+ requestParams: ticketSchema.omit("id").partial(),
8
+ responseBody: nu.array(ticketSchema),
9
9
  } satisfies RequestSchema;
@@ -3,4 +3,3 @@ export { getTicketSchema } from "./get-ticket";
3
3
  export { getTicketsSchema } from "./get-tickets";
4
4
  export { patchTicketSchema } from "./patch-ticket";
5
5
  export { postTicketSchema } from "./post-ticket";
6
- export { ticketBaseSchema } from "./ticket-base";
@@ -1,10 +1,10 @@
1
1
  import { idNumberSchema, type RequestSchema } from "@nubase/core";
2
- import { ticketBaseSchema } from "./ticket-base";
2
+ import { ticketSchema } from "../../resources/ticket";
3
3
 
4
4
  export const patchTicketSchema = {
5
5
  method: "PATCH" as const,
6
6
  path: "/tickets/:id",
7
7
  requestParams: idNumberSchema,
8
- requestBody: ticketBaseSchema.omit("id").partial(),
9
- responseBody: ticketBaseSchema,
8
+ requestBody: ticketSchema.omit("id").partial(),
9
+ responseBody: ticketSchema,
10
10
  } satisfies RequestSchema;
@@ -1,10 +1,10 @@
1
1
  import { emptySchema, type RequestSchema } from "@nubase/core";
2
- import { ticketBaseSchema } from "./ticket-base";
2
+ import { ticketSchema } from "../../resources/ticket";
3
3
 
4
4
  export const postTicketSchema = {
5
5
  method: "POST" as const,
6
6
  path: "/tickets",
7
7
  requestParams: emptySchema,
8
- requestBody: ticketBaseSchema.omit("id"),
9
- responseBody: ticketBaseSchema,
8
+ requestBody: ticketSchema.omit("id"),
9
+ responseBody: ticketSchema,
10
10
  } satisfies RequestSchema;
@@ -1,3 +1,2 @@
1
1
  export * from "./api-endpoints";
2
- export * from "./schema/auth";
3
- export * from "./schema/ticket";
2
+ export * from "./resources";
@@ -0,0 +1,3 @@
1
+ export { ticketSchema } from "./ticket";
2
+ export { userSchema } from "./user";
3
+ export { workspaceSchema } from "./workspace";
@@ -1,6 +1,6 @@
1
1
  import { nu } from "@nubase/core";
2
2
 
3
- export const ticketBaseSchema = nu
3
+ export const ticketSchema = nu
4
4
  .object({
5
5
  id: nu.number(),
6
6
  title: nu.string().withMeta({
@@ -3,7 +3,7 @@ import { nu } from "@nubase/core";
3
3
  /**
4
4
  * Workspace info returned during login
5
5
  */
6
- export const workspaceInfoSchema = nu.object({
6
+ export const workspaceSchema = nu.object({
7
7
  id: nu.number(),
8
8
  slug: nu.string(),
9
9
  name: nu.string(),