Skip to content

Commit 2db19b2

Browse files
authored
fix(project): align metadata and ranking tabs (#1138)
* fix(project): align conversation metadata Extract shared inline metadata primitives for compact time and edited labels, and reuse them in feed identity metadata, project activity cards, and project conversation headers. Reuse the same metadata styling for translation controls so translated/original labels align visually with timestamp metadata. Move the project conversation header card into a focused component so breadcrumb/menu/title/body/status layout is no longer embedded in the full project conversation page. Keep the conversation action menu pinned to the breadcrumb row while the breadcrumb wraps independently. Expose createdAt and isEdited for project page activities so cards can show created-time metadata and Edited state without relying on updatedAt, which can reflect non-content updates. Deploy: agora, api * fix(agora): clean up ranking tabs Hide completed and canceled prioritization tabs for manual MaxDiff conversations, since those lifecycle categories are only useful for GitHub-linked items. Manual URLs targeting those tabs now fall back to Summary and manual conversations skip lifecycle fetches. Only make result rows clickable when the dialog adds information: a body, an external GitHub link, or a likely truncated compact title. Full result tabs now show uncropped titles. Clarify COCM copy across MaxDiff learn-more text in supported languages as an optional setup that is not currently enabled and can help correct unrepresentative participation by improving vote distribution across groups. Deploy: agora
1 parent 894734d commit 2db19b2

23 files changed

Lines changed: 815 additions & 650 deletions

services/agora/src/api/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5173,6 +5173,8 @@ export interface ApiV1ProjectPageFetchPost200ResponseActivitiesInner {
51735173
'slug': string;
51745174
'kind': ApiV1ProjectPageFetchPost200ResponseActivitiesInnerKindEnum;
51755175
'isClosed': boolean;
5176+
'createdAt': string;
5177+
'isEdited': boolean;
51765178
'title': string;
51775179
'bodyPlainText': string;
51785180
'originalContent': ApiV1ProjectPageFetchPost200ResponseActivitiesInnerOriginalContent;

services/agora/src/api/docs/ApiV1ProjectPageFetchPost200ResponseActivitiesInner.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Name | Type | Description | Notes
88
**slug** | **string** | | [default to undefined]
99
**kind** | **string** | | [default to undefined]
1010
**isClosed** | **boolean** | | [default to undefined]
11+
**createdAt** | **string** | | [default to undefined]
12+
**isEdited** | **boolean** | | [default to undefined]
1113
**title** | **string** | | [default to undefined]
1214
**bodyPlainText** | **string** | | [default to '']
1315
**originalContent** | [**ApiV1ProjectPageFetchPost200ResponseActivitiesInnerOriginalContent**](ApiV1ProjectPageFetchPost200ResponseActivitiesInnerOriginalContent.md) | | [default to undefined]
@@ -25,6 +27,8 @@ const instance: ApiV1ProjectPageFetchPost200ResponseActivitiesInner = {
2527
slug,
2628
kind,
2729
isClosed,
30+
createdAt,
31+
isEdited,
2832
title,
2933
bodyPlainText,
3034
originalContent,

services/agora/src/components/features/user/UserIdentityCard.vue

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
/>
2626
</div>
2727

28-
<div :style="{ fontSize: '0.75rem' }" class="timestamp-container">
29-
{{ timeAgo }}
30-
<template v-if="isEdited">
31-
<span class="bullet">•</span>
32-
<span>{{ t("edited") }}</span>
33-
</template>
28+
<div class="timestamp-container">
29+
<ContentMetadataLine
30+
:created-at="createdAt"
31+
:is-edited="isEdited"
32+
:edited-label="t('edited')"
33+
/>
3434
<template v-if="participationMode">
3535
<span class="bullet">•</span>
3636
<span
@@ -67,16 +67,16 @@
6767
import OrganizationImage from "src/components/account/OrganizationImage.vue";
6868
import UserAvatar from "src/components/account/UserAvatar.vue";
6969
import UserMetadata from "src/components/features/user/UserMetadata.vue";
70+
import ContentMetadataLine from "src/components/ui-library/ContentMetadataLine.vue";
7071
import { useComponentI18n } from "src/composables/ui/useComponentI18n";
71-
import { useLocalizedTimeAgo } from "src/composables/ui/useLocalizedTimeAgo";
7272
import type { ParticipationMode } from "src/shared/types/zod";
7373
7474
import {
7575
type UserIdentityCardTranslations,
7676
userIdentityCardTranslations,
7777
} from "./UserIdentityCard.i18n";
7878
79-
const props = defineProps<{
79+
defineProps<{
8080
userIdentity: string;
8181
authorVerified: boolean;
8282
createdAt: Date;
@@ -87,8 +87,6 @@ const props = defineProps<{
8787
participationMode?: ParticipationMode;
8888
}>();
8989
90-
const timeAgo = useLocalizedTimeAgo(() => props.createdAt);
91-
9290
const { t } = useComponentI18n<UserIdentityCardTranslations>(
9391
userIdentityCardTranslations
9492
);

services/agora/src/components/post/maxdiff/MaxDiffItemListSection.vue

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,15 @@
3535
v-for="(item, index) in displayItems"
3636
:key="item.slugId"
3737
class="item-row"
38-
@click="props.onClickItem({ title: item.title, body: item.body, externalUrl: item.externalUrl })"
38+
:class="{ 'item-row--clickable': canOpenItem(item) }"
39+
@click="handleItemClick(item)"
3940
>
4041
<span class="item-number">{{ index + 1 }}</span>
4142
<div class="item-details">
4243
<ZKHtmlContent
4344
class="item-content"
4445
:html-body="item.title"
45-
:compact-mode="true"
46+
:compact-mode="compactMode"
4647
:enable-links="false"
4748
content-role="title"
4849
/>
@@ -108,12 +109,45 @@ const props = defineProps<{
108109
}>();
109110
110111
const COMPACT_LIMIT = 3;
112+
const EXPANDABLE_COMPACT_TITLE_LENGTH = 220;
111113
112114
const displayItems = computed(() =>
113115
props.compactMode ? props.items.slice(0, COMPACT_LIMIT) : props.items,
114116
);
115117
116118
const hasMore = computed(() => props.items.length > COMPACT_LIMIT);
119+
120+
function plainTextFromHtml(html: string): string {
121+
return html.replace(/<[^>]*>/g, " ").replace(/&nbsp;/g, " ").trim();
122+
}
123+
124+
function hasBodyContent(body: string | null): boolean {
125+
return body !== null && plainTextFromHtml(body).length > 0;
126+
}
127+
128+
function canOpenItem(item: MaxDiffListItem): boolean {
129+
if (hasBodyContent(item.body)) {
130+
return true;
131+
}
132+
if (item.externalUrl !== null) {
133+
return true;
134+
}
135+
return (
136+
props.compactMode &&
137+
plainTextFromHtml(item.title).length > EXPANDABLE_COMPACT_TITLE_LENGTH
138+
);
139+
}
140+
141+
function handleItemClick(item: MaxDiffListItem): void {
142+
if (!canOpenItem(item)) {
143+
return;
144+
}
145+
props.onClickItem({
146+
title: item.title,
147+
body: item.body,
148+
externalUrl: item.externalUrl,
149+
});
150+
}
117151
</script>
118152

119153
<style scoped lang="scss">
@@ -146,8 +180,11 @@ const hasMore = computed(() => props.items.length > COMPACT_LIMIT);
146180
padding: 0.75rem 1rem;
147181
background: $app-background-color;
148182
border-radius: 8px;
149-
cursor: pointer;
150183
transition: background-color 0.2s ease;
184+
}
185+
186+
.item-row--clickable {
187+
cursor: pointer;
151188
152189
&:hover {
153190
background-color: rgba(0, 0, 0, 0.05);

0 commit comments

Comments
 (0)