Merge branch 'worktree-agent-af80e237' into Develop
# Conflicts: # .planning/REQUIREMENTS.md # .planning/STATE.md
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { useRef, useState } from "react";
|
||||
import { ProfileSection } from "../components/ProfileSection";
|
||||
import {
|
||||
useApiKeys,
|
||||
useAuth,
|
||||
@@ -219,7 +220,13 @@ function SettingsPage() {
|
||||
<h1 className="text-xl font-semibold text-gray-900">Settings</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-100 p-5 space-y-6">
|
||||
{auth?.user && (
|
||||
<div className="bg-white rounded-xl border border-gray-100 p-5 space-y-6">
|
||||
<ProfileSection />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-100 p-5 space-y-6 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900">Weight Unit</h3>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
useRemoveSetupItem,
|
||||
useSetup,
|
||||
useUpdateItemClassification,
|
||||
useUpdateSetup,
|
||||
} from "../../hooks/useSetups";
|
||||
import { LucideIcon } from "../../lib/iconData";
|
||||
|
||||
@@ -24,6 +25,7 @@ function SetupDetailPage() {
|
||||
const numericId = Number(setupId);
|
||||
const { data: setup, isLoading } = useSetup(numericId);
|
||||
const deleteSetup = useDeleteSetup();
|
||||
const updateSetup = useUpdateSetup(numericId);
|
||||
const removeItem = useRemoveSetupItem(numericId);
|
||||
const updateClassification = useUpdateItemClassification(numericId);
|
||||
|
||||
@@ -160,6 +162,32 @@ function SetupDetailPage() {
|
||||
</svg>
|
||||
Add Items
|
||||
</button>
|
||||
|
||||
{/* Public toggle */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateSetup.mutate({ isPublic: !setup.isPublic })}
|
||||
className={`inline-flex items-center gap-1.5 px-3 py-2 text-sm font-medium rounded-lg transition-colors ${
|
||||
setup.isPublic
|
||||
? "text-green-700 bg-green-50 hover:bg-green-100"
|
||||
: "text-gray-500 bg-gray-50 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M2 12h20" />
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||
</svg>
|
||||
{setup.isPublic ? "Public" : "Private"}
|
||||
</button>
|
||||
|
||||
<div className="flex-1" />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
|
||||
102
src/client/routes/users/$userId.tsx
Normal file
102
src/client/routes/users/$userId.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { PublicSetupCard } from "../../components/PublicSetupCard";
|
||||
import { usePublicProfile } from "../../hooks/useProfile";
|
||||
|
||||
export const Route = createFileRoute("/users/$userId")({
|
||||
component: PublicProfilePage,
|
||||
});
|
||||
|
||||
function PublicProfilePage() {
|
||||
const { userId } = Route.useParams();
|
||||
const numericId = Number(userId);
|
||||
const { data: profile, isLoading, isError } = usePublicProfile(numericId);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
||||
<div className="animate-pulse space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-20 h-20 rounded-full bg-gray-200" />
|
||||
<div className="space-y-2">
|
||||
<div className="h-6 bg-gray-200 rounded w-40" />
|
||||
<div className="h-4 bg-gray-200 rounded w-64" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-4 bg-gray-200 rounded w-24" />
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="h-24 bg-gray-200 rounded-xl" />
|
||||
<div className="h-24 bg-gray-200 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !profile) {
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center">
|
||||
<p className="text-gray-500">User not found.</p>
|
||||
<Link
|
||||
to="/"
|
||||
className="text-sm text-gray-500 hover:text-gray-700 mt-4 inline-block"
|
||||
>
|
||||
← Back to home
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const displayName = profile.displayName || `User #${profile.id}`;
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
|
||||
{/* Profile header */}
|
||||
<div className="flex items-center gap-5 mb-8">
|
||||
{profile.avatarUrl ? (
|
||||
<img
|
||||
src={`/uploads/${profile.avatarUrl}`}
|
||||
alt={displayName}
|
||||
className="w-20 h-20 rounded-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-20 h-20 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<svg
|
||||
className="w-10 h-10 text-gray-300"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="12" cy="7" r="4" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold text-gray-900">{displayName}</h1>
|
||||
{profile.bio && (
|
||||
<p className="text-sm text-gray-500 mt-1 max-w-md">{profile.bio}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Public setups */}
|
||||
<div>
|
||||
<h2 className="text-base font-medium text-gray-900 mb-4">
|
||||
Public Setups
|
||||
</h2>
|
||||
{profile.setups.length === 0 ? (
|
||||
<p className="text-sm text-gray-400 py-8 text-center">
|
||||
No public setups yet
|
||||
</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{profile.setups.map((setup) => (
|
||||
<PublicSetupCard key={setup.id} setup={setup} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user