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:
@@ -1631,6 +1631,11 @@ button:disabled,
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.edit-meta .user-profile-link {
|
||||
color: var(--ink-soft);
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.checklist-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@@ -1869,6 +1874,14 @@ button:disabled,
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.life-post__byline .user-profile-link {
|
||||
overflow: hidden;
|
||||
color: var(--ink);
|
||||
font-weight: 950;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.life-post__byline span {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
@@ -2277,6 +2290,11 @@ button:disabled,
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.life-comment__meta .user-profile-link {
|
||||
color: var(--ink);
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.life-comment.is-deleted .life-comment__meta strong {
|
||||
color: var(--muted);
|
||||
font-style: italic;
|
||||
@@ -3090,6 +3108,11 @@ button:disabled,
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.edit-history-summary .user-profile-link {
|
||||
color: var(--ink);
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.edit-history-summary time,
|
||||
.edit-timeline time {
|
||||
color: var(--muted);
|
||||
@@ -3275,6 +3298,11 @@ button:disabled,
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.edit-history-detail-meta .user-profile-link {
|
||||
color: var(--ink-soft);
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.entity-discussion-panel {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
@@ -3423,6 +3451,12 @@ button:disabled,
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.entity-discussion-comment__meta .user-profile-link {
|
||||
color: var(--ink);
|
||||
font-size: 14px;
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.entity-discussion-comment.is-deleted .entity-discussion-comment__meta strong {
|
||||
color: var(--muted);
|
||||
}
|
||||
@@ -4461,6 +4495,270 @@ button:disabled,
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.profile-public-layout,
|
||||
.profile-tab-panel,
|
||||
.profile-activity-list {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.profile-layout--loading {
|
||||
grid-template-columns: minmax(260px, 0.5fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.profile-card--wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.profile-card--soft {
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.profile-hero {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.profile-hero .profile-identity {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.profile-stat-strip,
|
||||
.profile-stat-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.profile-stat-strip {
|
||||
grid-column: 1 / -1;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.profile-stat-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
|
||||
}
|
||||
|
||||
.profile-stat-strip div,
|
||||
.profile-stat-grid div {
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface-soft);
|
||||
}
|
||||
|
||||
.profile-stat-strip dt,
|
||||
.profile-stat-grid dt {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.profile-stat-strip dd,
|
||||
.profile-stat-grid dd {
|
||||
margin: 4px 0 0;
|
||||
color: var(--pokemon-blue-deep);
|
||||
font-family: var(--font-display);
|
||||
font-size: 30px;
|
||||
font-weight: 950;
|
||||
line-height: 1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.profile-section-grid,
|
||||
.profile-account-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.profile-feed-card__metrics {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--line);
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.profile-feed-card__metrics span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.profile-feed-card__metrics .ui-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--pokemon-blue);
|
||||
}
|
||||
|
||||
.profile-load-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.profile-empty {
|
||||
min-height: 220px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 12px;
|
||||
padding: 26px;
|
||||
border: 1px dashed var(--line);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface-soft);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profile-empty--compact {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.profile-empty h2 {
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-family: var(--font-display);
|
||||
font-size: 22px;
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.profile-empty__icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
color: var(--pokemon-blue);
|
||||
}
|
||||
|
||||
.profile-contribution-list,
|
||||
.profile-contribution-row,
|
||||
.profile-activity-card,
|
||||
.profile-post-preview {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.profile-contribution-row,
|
||||
.profile-activity-card {
|
||||
min-width: 0;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface-soft);
|
||||
}
|
||||
|
||||
.profile-contribution-row > div,
|
||||
.profile-activity-card__header,
|
||||
.profile-post-preview__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.profile-contribution-row strong,
|
||||
.profile-post-preview strong,
|
||||
.profile-post-preview .user-profile-link {
|
||||
color: var(--ink);
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.profile-contribution-row span,
|
||||
.profile-activity-card time,
|
||||
.profile-post-preview span {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.profile-contribution-row dl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.profile-contribution-row dl div {
|
||||
min-width: 0;
|
||||
padding: 8px;
|
||||
border-radius: var(--radius-small);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.profile-contribution-row dt {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
.profile-contribution-row dd {
|
||||
margin: 3px 0 0;
|
||||
color: var(--ink);
|
||||
font-size: 18px;
|
||||
font-weight: 950;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.profile-activity-card__header span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: var(--ink-soft);
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.profile-activity-card__header .ui-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--pokemon-blue);
|
||||
}
|
||||
|
||||
.profile-post-preview {
|
||||
padding: 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.profile-post-preview p,
|
||||
.profile-comment-body,
|
||||
.profile-comment-excerpt {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
line-height: 1.6;
|
||||
overflow-wrap: anywhere;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.profile-comment-target {
|
||||
justify-self: start;
|
||||
color: var(--pokemon-blue-deep);
|
||||
font-weight: 950;
|
||||
}
|
||||
|
||||
.profile-comment-excerpt {
|
||||
padding: 10px 12px;
|
||||
border-left: 3px solid var(--pokemon-yellow);
|
||||
background: var(--surface);
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.user-profile-link,
|
||||
.profile-comment-target {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.user-profile-link:hover,
|
||||
.profile-comment-target:hover {
|
||||
color: var(--pokemon-blue-deep);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
|
||||
@@ -4877,11 +5175,19 @@ button:disabled,
|
||||
.pokemon-profile-row,
|
||||
.pokemon-related-grid,
|
||||
.profile-layout,
|
||||
.profile-layout--loading,
|
||||
.profile-section-grid,
|
||||
.profile-account-grid,
|
||||
.system-wording-layout,
|
||||
.admin-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.profile-hero,
|
||||
.profile-stat-strip {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.profile-card--referral {
|
||||
grid-column: auto;
|
||||
}
|
||||
@@ -5028,6 +5334,10 @@ button:disabled,
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.profile-contribution-row dl {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.life-toolbar__actions,
|
||||
.life-toolbar .ui-button {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user