feat(profile): add public user profiles with activity tabs and stats
Add API routes for user stats, posts, reactions, and comments Implement profile view with Feeds, Contributions, Reactions tabs Link to user profiles from edit history, discussions, and life posts Add database indexes to optimize user-centric queries
This commit is contained in:
@@ -324,6 +324,55 @@ export interface ReferralSummary {
|
||||
verifiedReferralCount: number;
|
||||
}
|
||||
|
||||
export interface PublicProfileUser extends UserSummary {
|
||||
joinedAt: string;
|
||||
}
|
||||
|
||||
export interface PublicProfileStats {
|
||||
wikiEdits: number;
|
||||
wikiCreates: number;
|
||||
wikiUpdates: number;
|
||||
wikiDeletes: number;
|
||||
imageUploads: number;
|
||||
lifePosts: number;
|
||||
lifeComments: number;
|
||||
lifeReactions: number;
|
||||
discussionComments: number;
|
||||
}
|
||||
|
||||
export interface PublicProfileContribution {
|
||||
contentType: string;
|
||||
total: number;
|
||||
creates: number;
|
||||
updates: number;
|
||||
deletes: number;
|
||||
lastContributedAt: string | null;
|
||||
}
|
||||
|
||||
export interface PublicUserProfile {
|
||||
user: PublicProfileUser;
|
||||
stats: PublicProfileStats;
|
||||
contributions: PublicProfileContribution[];
|
||||
}
|
||||
|
||||
export interface ProfileActivityParams {
|
||||
cursor?: string | null;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface UserReactionActivity {
|
||||
postId: number;
|
||||
reactionType: LifeReactionType;
|
||||
reactedAt: string;
|
||||
post: LifePost;
|
||||
}
|
||||
|
||||
export interface UserReactionActivityPage {
|
||||
items: UserReactionActivity[];
|
||||
nextCursor: string | null;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
export interface RoleSummary {
|
||||
id: number;
|
||||
key: string;
|
||||
@@ -508,6 +557,25 @@ export interface EntityDiscussionComment {
|
||||
replies: EntityDiscussionComment[];
|
||||
}
|
||||
|
||||
export interface UserCommentActivity {
|
||||
id: number;
|
||||
source: 'life' | 'discussion';
|
||||
body: string;
|
||||
createdAt: string;
|
||||
target: {
|
||||
type: 'life-post' | DiscussionEntityType;
|
||||
id: number;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UserCommentActivityPage {
|
||||
items: UserCommentActivity[];
|
||||
nextCursor: string | null;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
export interface EntityDiscussionCommentPayload {
|
||||
body: string;
|
||||
}
|
||||
@@ -694,6 +762,28 @@ export const api = {
|
||||
updateMe: (payload: UserProfilePayload) => sendJson<{ user: AuthUser }>('/api/auth/me', 'PATCH', payload),
|
||||
referral: () => getJson<{ referral: ReferralSummary }>('/api/auth/referral'),
|
||||
logout: () => postEmpty('/api/auth/logout'),
|
||||
publicProfile: (id: string | number) => getJson<{ profile: PublicUserProfile }>(`/api/users/${id}/profile`),
|
||||
userLifePosts: (id: string | number, params: ProfileActivityParams = {}) =>
|
||||
getJson<LifePostsPage>(
|
||||
`/api/users/${id}/life-posts${buildQuery({
|
||||
cursor: params.cursor ?? undefined,
|
||||
limit: params.limit
|
||||
})}`
|
||||
),
|
||||
userReactions: (id: string | number, params: ProfileActivityParams = {}) =>
|
||||
getJson<UserReactionActivityPage>(
|
||||
`/api/users/${id}/reactions${buildQuery({
|
||||
cursor: params.cursor ?? undefined,
|
||||
limit: params.limit
|
||||
})}`
|
||||
),
|
||||
userComments: (id: string | number, params: ProfileActivityParams = {}) =>
|
||||
getJson<UserCommentActivityPage>(
|
||||
`/api/users/${id}/comments${buildQuery({
|
||||
cursor: params.cursor ?? undefined,
|
||||
limit: params.limit
|
||||
})}`
|
||||
),
|
||||
adminUsers: () => getJson<AdminUser[]>('/api/admin/users'),
|
||||
updateAdminUserRoles: (id: string | number, roleIds: number[]) =>
|
||||
sendJson<AdminUser[]>(`/api/admin/users/${id}/roles`, 'PUT', { roleIds }),
|
||||
|
||||
Reference in New Issue
Block a user