Merge branch 'worktree-agent-adbc35a5' into Develop
# Conflicts: # .planning/STATE.md # drizzle-pg/meta/0002_snapshot.json # drizzle-pg/meta/_journal.json # src/db/schema.ts
This commit is contained in:
@@ -23,13 +23,16 @@ interface ItemGlobalLink {
|
||||
globalItemId: number;
|
||||
}
|
||||
|
||||
export function useGlobalItems(query?: string) {
|
||||
export function useGlobalItems(query?: string, tags?: string[]) {
|
||||
const params = new URLSearchParams();
|
||||
if (query) params.set("q", query);
|
||||
if (tags && tags.length > 0) params.set("tags", tags.join(","));
|
||||
const qs = params.toString();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["global-items", query ?? ""],
|
||||
queryKey: ["global-items", query ?? "", tags ?? []],
|
||||
queryFn: () =>
|
||||
apiGet<GlobalItem[]>(
|
||||
`/api/global-items${query ? `?q=${encodeURIComponent(query)}` : ""}`,
|
||||
),
|
||||
apiGet<GlobalItem[]>(`/api/global-items${qs ? `?${qs}` : ""}`),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
15
src/client/hooks/useTags.ts
Normal file
15
src/client/hooks/useTags.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiGet } from "../lib/api";
|
||||
|
||||
export interface Tag {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function useTags() {
|
||||
return useQuery({
|
||||
queryKey: ["tags"],
|
||||
queryFn: () => apiGet<Tag[]>("/api/tags"),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
}
|
||||
@@ -56,6 +56,17 @@ interface UIState {
|
||||
// Setup impact preview
|
||||
selectedSetupId: number | null;
|
||||
setSelectedSetupId: (id: number | null) => void;
|
||||
|
||||
// FAB menu
|
||||
fabMenuOpen: boolean;
|
||||
openFabMenu: () => void;
|
||||
closeFabMenu: () => void;
|
||||
|
||||
// Catalog search overlay
|
||||
catalogSearchOpen: boolean;
|
||||
catalogSearchMode: "collection" | "thread" | null;
|
||||
openCatalogSearch: (mode: "collection" | "thread") => void;
|
||||
closeCatalogSearch: () => void;
|
||||
}
|
||||
|
||||
export const useUIStore = create<UIState>((set) => ({
|
||||
@@ -119,4 +130,21 @@ export const useUIStore = create<UIState>((set) => ({
|
||||
// Setup impact preview
|
||||
selectedSetupId: null,
|
||||
setSelectedSetupId: (id) => set({ selectedSetupId: id }),
|
||||
|
||||
// FAB menu
|
||||
fabMenuOpen: false,
|
||||
openFabMenu: () => set({ fabMenuOpen: true }),
|
||||
closeFabMenu: () => set({ fabMenuOpen: false }),
|
||||
|
||||
// Catalog search overlay
|
||||
catalogSearchOpen: false,
|
||||
catalogSearchMode: null,
|
||||
openCatalogSearch: (mode) =>
|
||||
set({
|
||||
catalogSearchOpen: true,
|
||||
catalogSearchMode: mode,
|
||||
fabMenuOpen: false,
|
||||
}),
|
||||
closeCatalogSearch: () =>
|
||||
set({ catalogSearchOpen: false, catalogSearchMode: null }),
|
||||
}));
|
||||
|
||||
@@ -12,12 +12,14 @@ import { mcpRoutes } from "./mcp/index.ts";
|
||||
import { requireAuth } from "./middleware/auth.ts";
|
||||
import { authRoutes } from "./routes/auth.ts";
|
||||
import { categoryRoutes } from "./routes/categories.ts";
|
||||
import { globalItemRoutes } from "./routes/global-items.ts";
|
||||
import { imageRoutes } from "./routes/images.ts";
|
||||
import { itemRoutes } from "./routes/items.ts";
|
||||
import { oauthRoutes, wellKnownRoute } from "./routes/oauth.ts";
|
||||
import { profileRoutes } from "./routes/profiles.ts";
|
||||
import { settingsRoutes } from "./routes/settings.ts";
|
||||
import { setupRoutes } from "./routes/setups.ts";
|
||||
import { tagRoutes } from "./routes/tags.ts";
|
||||
import { threadRoutes } from "./routes/threads.ts";
|
||||
import { totalRoutes } from "./routes/totals.ts";
|
||||
|
||||
@@ -51,10 +53,7 @@ if (process.env.NODE_ENV !== "production") {
|
||||
if (setCookies.length > 0) {
|
||||
c.res.headers.delete("Set-Cookie");
|
||||
for (const cookie of setCookies) {
|
||||
c.res.headers.append(
|
||||
"Set-Cookie",
|
||||
cookie.replace(/;\s*Secure/gi, ""),
|
||||
);
|
||||
c.res.headers.append("Set-Cookie", cookie.replace(/;\s*Secure/gi, ""));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -98,6 +97,12 @@ app.use("/api/*", async (c, next) => {
|
||||
// Skip public setup view (GET /api/setups/:id/public)
|
||||
if (/^\/api\/setups\/\d+\/public$/.test(c.req.path) && c.req.method === "GET")
|
||||
return next();
|
||||
// Skip public tags endpoint (GET /api/tags)
|
||||
if (c.req.path.startsWith("/api/tags") && c.req.method === "GET")
|
||||
return next();
|
||||
// Skip public global-items endpoint (GET /api/global-items)
|
||||
if (c.req.path.startsWith("/api/global-items") && c.req.method === "GET")
|
||||
return next();
|
||||
// All other methods require auth for userId resolution
|
||||
return requireAuth(c, next);
|
||||
});
|
||||
@@ -112,6 +117,8 @@ app.route("/api/settings", settingsRoutes);
|
||||
app.route("/api/threads", threadRoutes);
|
||||
app.route("/api/users", profileRoutes);
|
||||
app.route("/api/setups", setupRoutes);
|
||||
app.route("/api/global-items", globalItemRoutes);
|
||||
app.route("/api/tags", tagRoutes);
|
||||
|
||||
// MCP server (conditionally mounted)
|
||||
if (process.env.GEARBOX_MCP !== "false") {
|
||||
|
||||
14
src/server/routes/tags.ts
Normal file
14
src/server/routes/tags.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Hono } from "hono";
|
||||
import { getAllTags } from "../services/tag.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const allTags = await getAllTags(db);
|
||||
return c.json(allTags);
|
||||
});
|
||||
|
||||
export { app as tagRoutes };
|
||||
12
src/server/services/tag.service.ts
Normal file
12
src/server/services/tag.service.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { asc } from "drizzle-orm";
|
||||
import { db as prodDb } from "../../db/index.ts";
|
||||
import { tags } from "../../db/schema.ts";
|
||||
|
||||
type Db = typeof prodDb;
|
||||
|
||||
export async function getAllTags(db: Db = prodDb) {
|
||||
return db
|
||||
.select({ id: tags.id, name: tags.name })
|
||||
.from(tags)
|
||||
.orderBy(asc(tags.name));
|
||||
}
|
||||
Reference in New Issue
Block a user