From 4044665c9654aabdaea884eada4d1a2d2575b82a Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 8 Sep 2025 12:41:40 +0000 Subject: [PATCH] live data shows from database now make buttons work@ --- src/app/api/portfolio/[portfolioId]/route.ts | 41 +++++++- .../settings/PortfolioSettings.tsx | 2 +- .../settings/UsersPermissionsCard.tsx | 99 +++++++++++++------ .../[slug]/(portfolio)/settings/roles.tsx | 1 + 4 files changed, 112 insertions(+), 31 deletions(-) diff --git a/src/app/api/portfolio/[portfolioId]/route.ts b/src/app/api/portfolio/[portfolioId]/route.ts index a5286e3..9572f89 100644 --- a/src/app/api/portfolio/[portfolioId]/route.ts +++ b/src/app/api/portfolio/[portfolioId]/route.ts @@ -1,6 +1,7 @@ import { db } from "@/app/db/db"; import { NextRequest, NextResponse } from "next/server"; -import { portfolio, portfolioUsers } from "@/app/db/schema/portfolio"; +import { portfolio, portfolioUsers} from "@/app/db/schema/portfolio"; +import { user } from "@/app/db/schema/users"; import { recommendation, recommendationMaterials, @@ -161,3 +162,41 @@ export async function DELETE(request: NextRequest, props: { params: Promise<{ po ); } } + + +// Get colloborators (users) that have access to the portfolio +export async function GET( + _req: NextRequest, + props: { params: Promise<{ portfolioId: string }> } +) { + const { portfolioId } = await props.params; + + try { + const rows = await db + .select({ + userId: portfolioUsers.userId, + role: portfolioUsers.role, + name: user.firstName, + email: user.email, + }) + .from(portfolioUsers) + .leftJoin(user, eq(user.id, portfolioUsers.userId)) + .where(eq(portfolioUsers.portfolioId, BigInt(portfolioId))); + + // Explicitly normalize BigInts to strings + const collaborators = rows.map((r) => ({ + userId: r.userId ? r.userId.toString() : null, + role: r.role, + name: r.name ?? null, + email: r.email ?? "", + })); + + return NextResponse.json({ users: collaborators }, { status: 200 }); + } catch (err) { + console.error("GET /users error:", err); + return NextResponse.json( + { error: "Failed to fetch users" }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/src/app/portfolio/[slug]/(portfolio)/settings/PortfolioSettings.tsx b/src/app/portfolio/[slug]/(portfolio)/settings/PortfolioSettings.tsx index 048bdd3..8993094 100644 --- a/src/app/portfolio/[slug]/(portfolio)/settings/PortfolioSettings.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/settings/PortfolioSettings.tsx @@ -460,7 +460,7 @@ export default function PortfolioSettings({ - +
Danger Zone: diff --git a/src/app/portfolio/[slug]/(portfolio)/settings/UsersPermissionsCard.tsx b/src/app/portfolio/[slug]/(portfolio)/settings/UsersPermissionsCard.tsx index 7cd2f29..8b00d8e 100644 --- a/src/app/portfolio/[slug]/(portfolio)/settings/UsersPermissionsCard.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/settings/UsersPermissionsCard.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Table, TableBody, @@ -9,29 +11,68 @@ import { import { Input } from "@/app/shadcn_components/ui/input"; import { Button } from "@/app/shadcn_components/ui/button"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { Role, RoleDropdown, Collaborator } from "@/app/portfolio/[slug]/(portfolio)/settings/roles"; +import { useQuery } from "@tanstack/react-query"; -export function UsersPermissionsCard({ -}: { -}) { + +async function getPortfolioUsers(portfolioId: string): Promise { + const res = await fetch(`/api/portfolio/${portfolioId}`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + }); + if (!res.ok) throw new Error("Failed to fetch users"); + const json = await res.json(); + const users = Array.isArray(json) ? json : json.users; // support both shapes + + // Guard + shape to Collaborator[] + return Array.isArray(users) + ? users + .filter((u: any) => u.role !== "creator") // 👈 filter out creator + .map((u: any) => ({ + userId: String(u.userId), + name: u.name ?? null, + email: u.email ?? "", + role: u.role, + })) + : []; +} + +export function UsersPermissionsCard({ portfolioId }: { portfolioId: string }) { const [inviteEmail, setInviteEmail] = useState(""); const [inviteRole, setInviteRole] = useState("read"); - const collaborators: Collaborator[] = [ - { name: "Name 1", email: "name1@example.com", role: "read" }, - { name: "Name 2", email: "name2@example.com", role: "read" }, - ]; + + const { + data: collaborators = [], + isLoading, + isFetching, + refetch, + } = useQuery({ + queryKey: ["portfolioUsers", portfolioId], + queryFn: () => getPortfolioUsers(portfolioId), + enabled: !!portfolioId, // only run when id is present + refetchOnWindowFocus: false, // optional: avoid surprise refetch logs + onSuccess: (data) => { + console.log("Fetched users for portfolio:", data); + }, + onError: (err) => { + console.error("Error fetching users:", err); + }, + }); function handleInvite() { - console.log("handle invite") - } - - function onChangeRole(email:string, role:Role) { - console.log(`on change role ${email} ${role}`) + console.log("handle invite"); + // TODO: POST invite -> then refetch() } - function onRemove(email:string) { - console.log(`remove user ${email}`) + function onChangeRole(email: string, role: Role) { + console.log(`on change role ${email} ${role}`); + // TODO: PATCH role -> then refetch() + } + + function onRemove(email: string) { + console.log(`remove user ${email}`); + // TODO: DELETE user -> then refetch() } return ( @@ -43,6 +84,11 @@ export function UsersPermissionsCard({ Users Permission:

Add users and manage roles

+ + + {/* Invite row */} @@ -75,9 +121,7 @@ export function UsersPermissionsCard({ Current users -

- Update roles or remove access -

+

Update roles or remove access

@@ -91,7 +135,11 @@ export function UsersPermissionsCard({ - {collaborators.length === 0 ? ( + {isLoading ? ( + + Loading… + + ) : collaborators.length === 0 ? ( No users yet. @@ -103,17 +151,10 @@ export function UsersPermissionsCard({ {c.name || "—"} {c.email} - onChangeRole(c.email, r)} - /> + onChangeRole(c.email, r)} /> - @@ -129,4 +170,4 @@ export function UsersPermissionsCard({
); -} +} \ No newline at end of file diff --git a/src/app/portfolio/[slug]/(portfolio)/settings/roles.tsx b/src/app/portfolio/[slug]/(portfolio)/settings/roles.tsx index 97ecf3e..bd74b6c 100644 --- a/src/app/portfolio/[slug]/(portfolio)/settings/roles.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/settings/roles.tsx @@ -12,6 +12,7 @@ const ROLE_OPTIONS = ["read", "write"] as const; export type Role = typeof ROLE_OPTIONS[number]; export type Collaborator = { + userId: string; name?: string | null; email: string; role: Role;