mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-15 01:54:43 +00:00
Link commenter and following names to their user profile pages
This commit is contained in:
@@ -379,6 +379,7 @@ web/frontend/
|
|||||||
│ │ - catalog beers, beer styles
|
│ │ - catalog beers, beer styles
|
||||||
│ │ - home landing page
|
│ │ - home landing page
|
||||||
│ │ - theme theme switcher/guide
|
│ │ - theme theme switcher/guide
|
||||||
|
│ │ - users public user profile page (/users/:id)
|
||||||
│ ├── hooks/ Shared hooks not tied to one feature, for example useMediaQuery
|
│ ├── hooks/ Shared hooks not tied to one feature, for example useMediaQuery
|
||||||
│ ├── routes.ts Route table (React Router 8 config-based routing)
|
│ ├── routes.ts Route table (React Router 8 config-based routing)
|
||||||
│ ├── root.tsx App shell and global providers
|
│ ├── root.tsx App shell and global providers
|
||||||
@@ -395,6 +396,8 @@ web/frontend/
|
|||||||
`/breweries/:id`) with Leaflet-powered maps and geolocation-based "nearby"
|
`/breweries/:id`) with Leaflet-powered maps and geolocation-based "nearby"
|
||||||
search
|
search
|
||||||
- Render the auth demo, account dashboard, and theme guide routes
|
- Render the auth demo, account dashboard, and theme guide routes
|
||||||
|
- Render the public user profile route (`/users/:id`), including a user's
|
||||||
|
following list and (via brewery comments) links back to commenter profiles
|
||||||
- Manage cookie-backed website session state
|
- Manage cookie-backed website session state
|
||||||
- Call the .NET API for login, registration, token refresh, confirmation, and
|
- Call the .NET API for login, registration, token refresh, confirmation, and
|
||||||
brewery/location data
|
brewery/location data
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const commentItemDescription = `A single comment row on the brewery detail page:
|
|||||||
const comment: FillerComment = {
|
const comment: FillerComment = {
|
||||||
id: '1',
|
id: '1',
|
||||||
user: 'malt_kettle',
|
user: 'malt_kettle',
|
||||||
|
userAccountId: 'malt_kettle',
|
||||||
initials: 'MK',
|
initials: 'MK',
|
||||||
rating: 5,
|
rating: 5,
|
||||||
time: '2 days ago',
|
time: '2 days ago',
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Heart, HeartSolid } from 'iconoir-react';
|
import { Heart, HeartSolid } from 'iconoir-react';
|
||||||
|
import { Link } from 'react-router';
|
||||||
import StarRating from '../../shared/StarRating';
|
import StarRating from '../../shared/StarRating';
|
||||||
import type { FillerComment } from '../../../utils/filler-brewery-detail';
|
import type { FillerComment } from '../../../utils/filler-brewery-detail';
|
||||||
|
|
||||||
@@ -19,7 +20,9 @@ const CommentItem = ({ comment, onToggleLike }: CommentItemProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col gap-1 min-w-0">
|
<div className="flex-1 flex flex-col gap-1 min-w-0">
|
||||||
<div className="flex items-baseline gap-2 flex-wrap">
|
<div className="flex items-baseline gap-2 flex-wrap">
|
||||||
<span className="font-bold text-sm">{comment.user}</span>
|
<Link to={`/users/${comment.userAccountId}`} className="font-bold text-sm link link-hover">
|
||||||
|
{comment.user}
|
||||||
|
</Link>
|
||||||
<StarRating value={comment.rating} size="xs" />
|
<StarRating value={comment.rating} size="xs" />
|
||||||
<span className="text-xs text-base-content/60">{comment.time}</span>
|
<span className="text-xs text-base-content/60">{comment.time}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ export const loader = async ({ request, params }: Route.LoaderArgs) => {
|
|||||||
brewery,
|
brewery,
|
||||||
loggedIn: auth !== null,
|
loggedIn: auth !== null,
|
||||||
username: auth?.username ?? null,
|
username: auth?.username ?? null,
|
||||||
|
userAccountId: auth?.userAccountId ?? null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const initials = (username: string) => username.slice(0, 2).toUpperCase();
|
const initials = (username: string) => username.slice(0, 2).toUpperCase();
|
||||||
|
|
||||||
const BreweryDetail = ({ loaderData }: Route.ComponentProps) => {
|
const BreweryDetail = ({ loaderData }: Route.ComponentProps) => {
|
||||||
const { brewery, loggedIn, username } = loaderData;
|
const { brewery, loggedIn, username, userAccountId } = loaderData;
|
||||||
|
|
||||||
const [liked, setLiked] = useState(false);
|
const [liked, setLiked] = useState(false);
|
||||||
const [likeCount, setLikeCount] = useState(FILLER_BREWERY_META.likeCount);
|
const [likeCount, setLikeCount] = useState(FILLER_BREWERY_META.likeCount);
|
||||||
@@ -57,6 +58,7 @@ const BreweryDetail = ({ loaderData }: Route.ComponentProps) => {
|
|||||||
{
|
{
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
user: username ?? 'you',
|
user: username ?? 'you',
|
||||||
|
userAccountId: userAccountId ?? '',
|
||||||
initials: username ? initials(username) : 'YO',
|
initials: username ? initials(username) : 'YO',
|
||||||
rating: yourRating,
|
rating: yourRating,
|
||||||
time: 'just now',
|
time: 'just now',
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export const FILLER_BEERS: FillerBeer[] = [
|
|||||||
export interface FillerComment {
|
export interface FillerComment {
|
||||||
id: string;
|
id: string;
|
||||||
user: string;
|
user: string;
|
||||||
|
userAccountId: string;
|
||||||
initials: string;
|
initials: string;
|
||||||
rating: number;
|
rating: number;
|
||||||
time: string;
|
time: string;
|
||||||
@@ -100,6 +101,7 @@ export const FILLER_COMMENTS: FillerComment[] = [
|
|||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
user: 'malt_kettle',
|
user: 'malt_kettle',
|
||||||
|
userAccountId: 'malt_kettle',
|
||||||
initials: 'MK',
|
initials: 'MK',
|
||||||
rating: 5,
|
rating: 5,
|
||||||
time: '2 days ago',
|
time: '2 days ago',
|
||||||
@@ -110,6 +112,7 @@ export const FILLER_COMMENTS: FillerComment[] = [
|
|||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
user: 'stoutfan_88',
|
user: 'stoutfan_88',
|
||||||
|
userAccountId: 'stoutfan_88',
|
||||||
initials: 'SB',
|
initials: 'SB',
|
||||||
rating: 4,
|
rating: 4,
|
||||||
time: '1 week ago',
|
time: '1 week ago',
|
||||||
@@ -120,6 +123,7 @@ export const FILLER_COMMENTS: FillerComment[] = [
|
|||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
user: 'ale_annie',
|
user: 'ale_annie',
|
||||||
|
userAccountId: 'ale_annie',
|
||||||
initials: 'AL',
|
initials: 'AL',
|
||||||
rating: 4,
|
rating: 4,
|
||||||
time: '3 weeks ago',
|
time: '3 weeks ago',
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Link } from 'react-router';
|
||||||
import EmptyState from '../EmptyState';
|
import EmptyState from '../EmptyState';
|
||||||
import type { FillerFollowing } from '../../../utils/filler-user-profile';
|
import type { FillerFollowing } from '../../../utils/filler-user-profile';
|
||||||
|
|
||||||
@@ -31,7 +32,16 @@ const FollowingTab = ({ following, onToggleFollow }: FollowingTabProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
|
{f.userAccountId ? (
|
||||||
|
<Link
|
||||||
|
to={`/users/${f.userAccountId}`}
|
||||||
|
className="font-semibold text-sm truncate link link-hover block"
|
||||||
|
>
|
||||||
|
{f.name}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
<p className="font-semibold text-sm m-0 truncate">{f.name}</p>
|
<p className="font-semibold text-sm m-0 truncate">{f.name}</p>
|
||||||
|
)}
|
||||||
<p className="text-xs text-base-content/60 m-0 truncate">{f.meta}</p>
|
<p className="text-xs text-base-content/60 m-0 truncate">{f.meta}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -74,13 +74,14 @@ export interface FillerFollowing {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
meta: string;
|
meta: string;
|
||||||
|
userAccountId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FILLER_FOLLOWING: FillerFollowing[] = [
|
export const FILLER_FOLLOWING: FillerFollowing[] = [
|
||||||
{ id: '1', name: 'Breakside Brewery', meta: 'Portland, OR · Brewery' },
|
{ id: '1', name: 'Breakside Brewery', meta: 'Portland, OR · Brewery' },
|
||||||
{ id: '2', name: 'Jonah Weckstein', meta: '@jweck · 142 ratings' },
|
{ id: '2', name: 'Jonah Weckstein', meta: '@jweck · 142 ratings', userAccountId: 'jweck' },
|
||||||
{ id: '3', name: 'Wayfinder Beer', meta: 'Portland, OR · Brewery' },
|
{ id: '3', name: 'Wayfinder Beer', meta: 'Portland, OR · Brewery' },
|
||||||
{ id: '4', name: 'Casey Blume', meta: '@caseyb · 88 ratings' },
|
{ id: '4', name: 'Casey Blume', meta: '@caseyb · 88 ratings', userAccountId: 'caseyb' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface FillerLiked {
|
export interface FillerLiked {
|
||||||
|
|||||||
Reference in New Issue
Block a user