feat(28-03): add profile navigation link and extend /me with createdAt
Adds Profile link to UserMenu dropdown (above Settings), extends /me endpoint to return user's createdAt for member-since display, and updates AuthState interface with optional createdAt field. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,18 @@ export function UserMenu() {
|
|||||||
</button>
|
</button>
|
||||||
{open && (
|
{open && (
|
||||||
<div className="absolute right-0 mt-1 w-40 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50">
|
<div className="absolute right-0 mt-1 w-40 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50">
|
||||||
|
<Link
|
||||||
|
to="/profile"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
className="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
|
<LucideIcon
|
||||||
|
name="circle-user"
|
||||||
|
size={16}
|
||||||
|
className="text-gray-400"
|
||||||
|
/>
|
||||||
|
Profile
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/settings"
|
to="/settings"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { apiDelete, apiGet, apiPost } from "../lib/api";
|
import { apiDelete, apiGet, apiPost } from "../lib/api";
|
||||||
|
|
||||||
interface AuthState {
|
interface AuthState {
|
||||||
user: { id: string; email?: string } | null;
|
user: { id: string; email?: string; createdAt?: string } | null;
|
||||||
authenticated: boolean;
|
authenticated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { getAuth } from "@hono/oidc-auth";
|
import { getAuth } from "@hono/oidc-auth";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { users } from "../../db/schema.ts";
|
||||||
import { updateProfileSchema } from "../../shared/schemas.ts";
|
import { updateProfileSchema } from "../../shared/schemas.ts";
|
||||||
import { parseId } from "../lib/params.ts";
|
import { parseId } from "../lib/params.ts";
|
||||||
import { requireAuth } from "../middleware/auth.ts";
|
import { requireAuth } from "../middleware/auth.ts";
|
||||||
@@ -26,8 +28,17 @@ app.get("/me", async (c) => {
|
|||||||
if (auth) {
|
if (auth) {
|
||||||
const db = c.get("db");
|
const db = c.get("db");
|
||||||
const user = await getOrCreateUser(db, auth.sub);
|
const user = await getOrCreateUser(db, auth.sub);
|
||||||
|
// Get full user record for createdAt (member-since display on profile page)
|
||||||
|
const [fullUser] = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.id, user.id));
|
||||||
return c.json({
|
return c.json({
|
||||||
user: { id: user.id, email: auth.email },
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
email: auth.email,
|
||||||
|
createdAt: fullUser?.createdAt?.toISOString() ?? null,
|
||||||
|
},
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user