From 795f5939f546b97660d1b77321492302c36d4070 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Thu, 19 Mar 2026 14:47:30 +0800 Subject: [PATCH 01/11] Make dashboard page localizable --- apps/frontend/src/locales/en-US/index.json | 252 +++++++++++++ .../src/pages/dashboard/affiliate-links.vue | 11 +- .../src/pages/dashboard/analytics.vue | 6 +- .../src/pages/dashboard/collections.vue | 57 ++- apps/frontend/src/pages/dashboard/index.vue | 85 +++-- .../src/pages/dashboard/notifications.vue | 119 ++++++- .../src/pages/dashboard/organizations.vue | 44 ++- .../frontend/src/pages/dashboard/projects.vue | 330 +++++++++++++----- .../src/pages/dashboard/report/[id].vue | 21 +- apps/frontend/src/pages/dashboard/reports.vue | 19 +- .../src/pages/dashboard/revenue/transfers.vue | 105 +++++- 11 files changed, 883 insertions(+), 166 deletions(-) diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index 3c66ea3398..ce5c514bd3 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -554,6 +554,9 @@ "dashboard.affiliate-links.create.button": { "message": "Create affiliate link" }, + "dashboard.affiliate-links.empty.no-codes": { + "message": "No affiliate codes found." + }, "dashboard.affiliate-links.error.title": { "message": "Error loading affiliate links" }, @@ -572,6 +575,15 @@ "dashboard.affiliate-links.search": { "message": "Search affiliate links..." }, + "dashboard.analytics.from-projects": { + "message": "from {count} {count, plural, one {project} other {projects}}" + }, + "dashboard.analytics.total-downloads": { + "message": "Total downloads" + }, + "dashboard.analytics.total-followers": { + "message": "Total followers" + }, "dashboard.collections.button.create-new": { "message": "Create new" }, @@ -596,6 +608,21 @@ "dashboard.collections.long-title": { "message": "Your collections" }, + "dashboard.collections.placeholder.search": { + "message": "Search collections..." + }, + "dashboard.collections.sort.control-name": { + "message": "Sort by" + }, + "dashboard.collections.sort.name-ascending": { + "message": "Name (A-Z)" + }, + "dashboard.collections.sort.recently-created": { + "message": "Recently Created" + }, + "dashboard.collections.sort.recently-updated": { + "message": "Recently Updated" + }, "dashboard.creator-tax-form-modal.confirmation.download-button": { "message": "Download {formType}" }, @@ -848,6 +875,189 @@ "dashboard.creator-withdraw-modal.withdraw-limit-used": { "message": "You've used up your {withdrawLimit} withdrawal limit. You must complete a tax form to withdraw more." }, + "dashboard.head-title": { + "message": "Dashboard" + }, + "dashboard.notifications.button.mark-all-as-read": { + "message": "Mark all as read" + }, + "dashboard.notifications.button.view-history": { + "message": "View history" + }, + "dashboard.notifications.empty.no-unread": { + "message": "You don't have any unread notifications." + }, + "dashboard.notifications.error.loading": { + "message": "Error loading notifications:" + }, + "dashboard.notifications.history.label": { + "message": "History" + }, + "dashboard.notifications.history.title": { + "message": "Notification history" + }, + "dashboard.notifications.link.see-all": { + "message": "See all" + }, + "dashboard.notifications.link.view-history": { + "message": "View notification history" + }, + "dashboard.notifications.link.view-more": { + "message": "View {extraNotifs} more {extraNotifs, plural, one {notification} other {notifications}}" + }, + "dashboard.notifications.loading": { + "message": "Loading notifications..." + }, + "dashboard.notifications.type.moderator-messages": { + "message": "Moderator messages" + }, + "dashboard.notifications.type.organization-invites": { + "message": "Organization invites" + }, + "dashboard.notifications.type.other": { + "message": "Other notifications" + }, + "dashboard.notifications.type.project-updates": { + "message": "Project updates" + }, + "dashboard.notifications.type.status-changes": { + "message": "Status changes" + }, + "dashboard.notifications.type.team-invites": { + "message": "Team invites" + }, + "dashboard.organizations.button.create": { + "message": "Create organization" + }, + "dashboard.organizations.empty.cta": { + "message": "Make an organization!" + }, + "dashboard.organizations.error.fetch": { + "message": "Failed to fetch organizations" + }, + "dashboard.organizations.member-count": { + "message": "{count} {count, plural, one {member} other {members}}" + }, + "dashboard.organizations.title": { + "message": "Organizations" + }, + "dashboard.projects.bulk-edit-hint": { + "message": "You can edit multiple projects at once by selecting them below." + }, + "dashboard.projects.bulk-edit.server-disabled": { + "message": "Server projects do not support bulk editing" + }, + "dashboard.projects.empty": { + "message": "You don't have any projects yet. Click the green button above to begin." + }, + "dashboard.projects.head-title": { + "message": "Projects" + }, + "dashboard.projects.links.and-more": { + "message": "and {count} more..." + }, + "dashboard.projects.links.button.clear-link": { + "message": "Clear link" + }, + "dashboard.projects.links.button.edit": { + "message": "Edit links" + }, + "dashboard.projects.links.changes-applied": { + "message": "Changes will be applied to {count} {count, plural, one {project} other {projects}}." + }, + "dashboard.projects.links.description": { + "message": "Any links you specify below will be overwritten on each of the selected projects. Any you leave blank will be ignored. You can clear a link from all selected projects using the trash can button." + }, + "dashboard.projects.links.discord-invite.description": { + "message": "An invitation link to your Discord server." + }, + "dashboard.projects.links.discord-invite.label": { + "message": "Discord invite" + }, + "dashboard.projects.links.issue-tracker.description": { + "message": "A place for users to report bugs, issues, and concerns about your project." + }, + "dashboard.projects.links.issue-tracker.label": { + "message": "Issue tracker" + }, + "dashboard.projects.links.placeholder.cleared": { + "message": "Existing link will be cleared" + }, + "dashboard.projects.links.placeholder.valid-discord-url": { + "message": "Enter a valid Discord invite URL" + }, + "dashboard.projects.links.placeholder.valid-url": { + "message": "Enter a valid URL" + }, + "dashboard.projects.links.show-all-projects": { + "message": "Show all projects" + }, + "dashboard.projects.links.source-code.description": { + "message": "A page/repository containing the source code for your project" + }, + "dashboard.projects.links.source-code.label": { + "message": "Source code" + }, + "dashboard.projects.links.wiki-page.description": { + "message": "A page containing information, documentation, and help for the project." + }, + "dashboard.projects.links.wiki-page.label": { + "message": "Wiki page" + }, + "dashboard.projects.notification.bulk-edit-success": { + "message": "Bulk edited selected project's links." + }, + "dashboard.projects.project.icon-alt": { + "message": "Icon for {title}" + }, + "dashboard.projects.project.moderator-message-aria": { + "message": "Project has a message from the moderators. View the project to see more." + }, + "dashboard.projects.project.review-environment-metadata": { + "message": "Please review environment metadata" + }, + "dashboard.projects.sort.ascending": { + "message": "Ascending" + }, + "dashboard.projects.sort.descending": { + "message": "Descending" + }, + "dashboard.projects.sort.label": { + "message": "Sort by" + }, + "dashboard.projects.sort.option.name": { + "message": "Name" + }, + "dashboard.projects.sort.option.status": { + "message": "Status" + }, + "dashboard.projects.sort.option.type": { + "message": "Type" + }, + "dashboard.projects.table.icon": { + "message": "Icon" + }, + "dashboard.projects.table.id": { + "message": "ID" + }, + "dashboard.projects.table.name": { + "message": "Name" + }, + "dashboard.projects.table.status": { + "message": "Status" + }, + "dashboard.projects.table.type": { + "message": "Type" + }, + "dashboard.report.title": { + "message": "Report {id}" + }, + "dashboard.reports.active-title": { + "message": "Active reports" + }, + "dashboard.reports.title": { + "message": "Reports" + }, "dashboard.revenue.available-now": { "message": "Available now" }, @@ -884,6 +1094,36 @@ "dashboard.revenue.transactions.btn.download-csv": { "message": "Download as CSV" }, + "dashboard.revenue.transactions.csv.header.amount": { + "message": "Amount" + }, + "dashboard.revenue.transactions.csv.header.date": { + "message": "Date" + }, + "dashboard.revenue.transactions.csv.header.fee": { + "message": "Fee" + }, + "dashboard.revenue.transactions.csv.header.source": { + "message": "Source" + }, + "dashboard.revenue.transactions.csv.header.status": { + "message": "Status" + }, + "dashboard.revenue.transactions.csv.header.type": { + "message": "Type" + }, + "dashboard.revenue.transactions.csv.source.mural-pay-unknown": { + "message": "Mural Pay ({unknown})" + }, + "dashboard.revenue.transactions.csv.type.payout": { + "message": "Payout" + }, + "dashboard.revenue.transactions.csv.type.withdrawal": { + "message": "Withdrawal" + }, + "dashboard.revenue.transactions.head-title": { + "message": "Transaction history" + }, "dashboard.revenue.transactions.header": { "message": "Transactions" }, @@ -893,9 +1133,21 @@ "dashboard.revenue.transactions.none.desc": { "message": "Your payouts and withdrawals will appear here." }, + "dashboard.revenue.transactions.not-applicable": { + "message": "N/A" + }, + "dashboard.revenue.transactions.period.last-month": { + "message": "Last month" + }, + "dashboard.revenue.transactions.period.this-month": { + "message": "This month" + }, "dashboard.revenue.transactions.see-all": { "message": "See all" }, + "dashboard.revenue.transactions.year.all": { + "message": "All years" + }, "dashboard.revenue.withdraw.blocked-tin-mismatch": { "message": "Your withdrawals are temporarily locked because your TIN or SSN didn't match IRS records. Please contact support to reset and resubmit your tax form." }, diff --git a/apps/frontend/src/pages/dashboard/affiliate-links.vue b/apps/frontend/src/pages/dashboard/affiliate-links.vue index 708f0d0592..9221e21c3d 100644 --- a/apps/frontend/src/pages/dashboard/affiliate-links.vue +++ b/apps/frontend/src/pages/dashboard/affiliate-links.vue @@ -44,7 +44,7 @@ v-else-if="!filteredAffiliates || filteredAffiliates.length === 0" class="py-8 text-center" > -

No affiliate codes found.

+

{{ formatMessage(messages.noAffiliateCodesFound) }}

+ auth.value?.user ? (auth.value.user as { id: string }).id : null, +) const filteredAffiliates = computed( () => affiliateLinks.value?.filter( (link: Labrinth.Affiliate.Internal.AffiliateCode) => - link.affiliate === auth.value?.user?.id && + link.affiliate === currentUserId.value && (filterQuery.value.trim() ? link.source_name.trim().toLowerCase().includes(filterQuery.value.trim().toLowerCase()) : true), @@ -166,6 +169,10 @@ const messages = defineMessages({ id: 'dashboard.affiliate-links.error.title', defaultMessage: 'Error loading affiliate links', }, + noAffiliateCodesFound: { + id: 'dashboard.affiliate-links.empty.no-codes', + defaultMessage: 'No affiliate codes found.', + }, revokeConfirmButton: { id: 'dashboard.affiliate-links.revoke-confirm.button', defaultMessage: 'Revoke', diff --git a/apps/frontend/src/pages/dashboard/analytics.vue b/apps/frontend/src/pages/dashboard/analytics.vue index 0301240f1b..3069ac7615 100644 --- a/apps/frontend/src/pages/dashboard/analytics.vue +++ b/apps/frontend/src/pages/dashboard/analytics.vue @@ -5,17 +5,19 @@ diff --git a/apps/frontend/src/pages/dashboard/reports.vue b/apps/frontend/src/pages/dashboard/reports.vue index 98a9908ffc..e538b5e480 100644 --- a/apps/frontend/src/pages/dashboard/reports.vue +++ b/apps/frontend/src/pages/dashboard/reports.vue @@ -1,16 +1,31 @@ diff --git a/apps/frontend/src/pages/dashboard/revenue/transfers.vue b/apps/frontend/src/pages/dashboard/revenue/transfers.vue index 60cf33e3ed..745ad307bc 100644 --- a/apps/frontend/src/pages/dashboard/revenue/transfers.vue +++ b/apps/frontend/src/pages/dashboard/revenue/transfers.vue @@ -8,7 +8,9 @@ @@ -90,6 +92,7 @@ import { import { ButtonStyled, Combobox, + commonMessages, defineMessages, EmptyState, injectModrinthClient, @@ -97,7 +100,6 @@ import { useFormatMoney, useVIntl, } from '@modrinth/ui' -import { capitalizeString } from '@modrinth/utils' import { useQuery } from '@tanstack/vue-query' import dayjs from 'dayjs' @@ -116,7 +118,7 @@ const client = injectModrinthClient() const generatedState = useGeneratedState() useHead({ - title: 'Transaction history - Modrinth', + title: () => `${formatMessage(messages.headTitle)} - Modrinth`, }) const { data: transactions, refetch } = useQuery({ @@ -143,7 +145,7 @@ const yearOptions = computed(() => { return yearValues.map((year) => ({ value: year, - label: year === 'all' ? 'All years' : String(year), + label: year === 'all' ? formatMessage(messages.allYears) : String(year), })) }) @@ -161,11 +163,11 @@ function getPeriodLabel(date) { const now = dayjs() if (txnDate.isSame(now, 'month')) { - return 'This month' + return formatMessage(messages.thisMonth) } else if (txnDate.isSame(now.subtract(1, 'month'), 'month')) { - return 'Last month' + return formatMessage(messages.lastMonth) } else { - return capitalizeString(formatMonth(txnDate.toDate())) + return formatMonth(txnDate.toDate()) } } @@ -212,18 +214,28 @@ function transactionsToCSV() { } const newline = '\n' - const header = ['Date', 'Type', 'Source', 'Status', 'Amount', 'Fee'].join(',') + const header = [ + formatMessage(messages.csvDateHeader), + formatMessage(messages.csvTypeHeader), + formatMessage(messages.csvSourceHeader), + formatMessage(messages.csvStatusHeader), + formatMessage(messages.csvAmountHeader), + formatMessage(messages.csvFeeHeader), + ].join(',') const rows = filteredTransactions.value.map((txn) => { const date = dayjs(txn.created).format('YYYY-MM-DD HH:mm:ss') - const type = txn.type === 'withdrawal' ? 'Withdrawal' : 'Payout' + const type = + txn.type === 'withdrawal' + ? formatMessage(messages.csvTypeWithdrawal) + : formatMessage(messages.csvTypePayout) let methodOrSource = '' let status = '' let fee = '' if (txn.type === 'withdrawal') { - const method = txn.method_type || txn.method || 'Unknown' + const method = txn.method_type || txn.method || 'unknown' switch (method) { case 'paypal': methodOrSource = 'PayPal' @@ -249,15 +261,20 @@ function transactionsToCSV() { break } } - methodOrSource = 'Mural Pay (Unknown)' + methodOrSource = formatMessage(messages.muralPayUnknown, { + unknown: formatMessage(commonMessages.unknownLabel), + }) break default: - methodOrSource = method.charAt(0).toUpperCase() + method.slice(1) + methodOrSource = + method === 'unknown' + ? formatMessage(commonMessages.unknownLabel) + : method.charAt(0).toUpperCase() + method.slice(1) } status = txn.status ? txn.status.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()) - : 'Unknown' + : formatMessage(commonMessages.unknownLabel) fee = txn.fee ? Number(txn.fee).toFixed(2) : '0.00' } else { @@ -266,9 +283,9 @@ function transactionsToCSV() { .split('_') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ') - : 'Unknown' - status = 'N/A' - fee = 'N/A' + : formatMessage(commonMessages.unknownLabel) + status = formatMessage(messages.notApplicable) + fee = formatMessage(messages.notApplicable) } const amount = Number(txn.amount).toFixed(2) @@ -322,6 +339,10 @@ const messages = defineMessages({ id: 'dashboard.revenue.transactions.header', defaultMessage: 'Transactions', }, + headTitle: { + id: 'dashboard.revenue.transactions.head-title', + defaultMessage: 'Transaction history', + }, received: { id: 'dashboard.revenue.stats.received', defaultMessage: 'Received', @@ -346,5 +367,57 @@ const messages = defineMessages({ id: 'dashboard.revenue.transactions.btn.download-csv', defaultMessage: 'Download as CSV', }, + allYears: { + id: 'dashboard.revenue.transactions.year.all', + defaultMessage: 'All years', + }, + thisMonth: { + id: 'dashboard.revenue.transactions.period.this-month', + defaultMessage: 'This month', + }, + lastMonth: { + id: 'dashboard.revenue.transactions.period.last-month', + defaultMessage: 'Last month', + }, + notApplicable: { + id: 'dashboard.revenue.transactions.not-applicable', + defaultMessage: 'N/A', + }, + csvDateHeader: { + id: 'dashboard.revenue.transactions.csv.header.date', + defaultMessage: 'Date', + }, + csvTypeHeader: { + id: 'dashboard.revenue.transactions.csv.header.type', + defaultMessage: 'Type', + }, + csvSourceHeader: { + id: 'dashboard.revenue.transactions.csv.header.source', + defaultMessage: 'Source', + }, + csvStatusHeader: { + id: 'dashboard.revenue.transactions.csv.header.status', + defaultMessage: 'Status', + }, + csvAmountHeader: { + id: 'dashboard.revenue.transactions.csv.header.amount', + defaultMessage: 'Amount', + }, + csvFeeHeader: { + id: 'dashboard.revenue.transactions.csv.header.fee', + defaultMessage: 'Fee', + }, + csvTypeWithdrawal: { + id: 'dashboard.revenue.transactions.csv.type.withdrawal', + defaultMessage: 'Withdrawal', + }, + csvTypePayout: { + id: 'dashboard.revenue.transactions.csv.type.payout', + defaultMessage: 'Payout', + }, + muralPayUnknown: { + id: 'dashboard.revenue.transactions.csv.source.mural-pay-unknown', + defaultMessage: 'Mural Pay ({unknown})', + }, }) From b9f7b6b0d4f017046e3584f016a2f44232d06527 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Fri, 20 Mar 2026 16:56:08 +0800 Subject: [PATCH 02/11] dashboard sidebar --- apps/frontend/src/pages/dashboard.vue | 59 ++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/apps/frontend/src/pages/dashboard.vue b/apps/frontend/src/pages/dashboard.vue index de3797c3d7..9f380c36c6 100644 --- a/apps/frontend/src/pages/dashboard.vue +++ b/apps/frontend/src/pages/dashboard.vue @@ -3,26 +3,26 @@
@@ -43,7 +43,7 @@ import { OrganizationIcon, ReportIcon, } from '@modrinth/assets' -import { commonMessages, useVIntl } from '@modrinth/ui' +import { defineMessages, commonMessages, useVIntl } from '@modrinth/ui' import { type User, UserBadge } from '@modrinth/utils' import NavStack from '~/components/ui/NavStack.vue' @@ -56,6 +56,45 @@ const isAffiliate = computed(() => { const { formatMessage } = useVIntl() +const messages = defineMessages({ + dashboard: { + id: 'dashboard.sidebar.label.dashboard', + defaultMessage: 'Dashboard', + }, + overview: { + id: 'dashboard.sidebar.label.overview', + defaultMessage: 'Overview', + }, + notifications: { + id: 'dashboard.sidebar.label.notifications', + defaultMessage: 'Notifications', + }, + activeReports: { + id: 'dashboard.sidebar.label.activeReports', + defaultMessage: 'Active reports', + }, + creators: { + id: 'dashboard.sidebar.label.creators', + defaultMessage: 'Creators', + }, + projects: { + id: 'dashboard.sidebar.label.projects', + defaultMessage: 'Projects', + }, + organizations: { + id: 'dashboard.sidebar.label.organizations', + defaultMessage: 'Organizations', + }, + analytics: { + id: 'dashboard.sidebar.label.analytics', + defaultMessage: 'Analytics', + }, + revenue: { + id: 'dashboard.sidebar.label.revenue', + defaultMessage: 'Revenue', + }, +}) + definePageMeta({ middleware: 'auth', }) From df2919d87268cc985c497ec68fc4890b4b8bcea6 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Tue, 24 Mar 2026 20:35:12 +0800 Subject: [PATCH 03/11] prepr:frontend --- apps/frontend/src/locales/en-US/index.json | 33 +++++++++++++++---- apps/frontend/src/pages/dashboard.vue | 33 +++++++++++++++---- .../src/pages/dashboard/collections.vue | 6 +--- .../frontend/src/pages/dashboard/projects.vue | 6 +--- .../src/pages/dashboard/revenue/index.vue | 30 +++++++++++++---- 5 files changed, 80 insertions(+), 28 deletions(-) diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index ce5c514bd3..6d07fb2a77 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -611,9 +611,6 @@ "dashboard.collections.placeholder.search": { "message": "Search collections..." }, - "dashboard.collections.sort.control-name": { - "message": "Sort by" - }, "dashboard.collections.sort.name-ascending": { "message": "Name (A-Z)" }, @@ -1022,9 +1019,6 @@ "dashboard.projects.sort.descending": { "message": "Descending" }, - "dashboard.projects.sort.label": { - "message": "Sort by" - }, "dashboard.projects.sort.option.name": { "message": "Name" }, @@ -1160,6 +1154,33 @@ "dashboard.revenue.withdraw.header": { "message": "Withdraw" }, + "dashboard.sidebar.label.activeReports": { + "message": "Active reports" + }, + "dashboard.sidebar.label.analytics": { + "message": "Analytics" + }, + "dashboard.sidebar.label.creators": { + "message": "Creators" + }, + "dashboard.sidebar.label.dashboard": { + "message": "Dashboard" + }, + "dashboard.sidebar.label.notifications": { + "message": "Notifications" + }, + "dashboard.sidebar.label.organizations": { + "message": "Organizations" + }, + "dashboard.sidebar.label.overview": { + "message": "Overview" + }, + "dashboard.sidebar.label.projects": { + "message": "Projects" + }, + "dashboard.sidebar.label.revenue": { + "message": "Revenue" + }, "dashboard.withdraw.completion.account": { "message": "Account" }, diff --git a/apps/frontend/src/pages/dashboard.vue b/apps/frontend/src/pages/dashboard.vue index 9f380c36c6..2be324416d 100644 --- a/apps/frontend/src/pages/dashboard.vue +++ b/apps/frontend/src/pages/dashboard.vue @@ -5,8 +5,16 @@ :items="[ { type: 'heading', label: formatMessage(messages.dashboard) }, { link: '/dashboard', label: formatMessage(messages.overview), icon: DashboardIcon }, - { link: '/dashboard/notifications', label: formatMessage(messages.notifications), icon: NotificationsIcon }, - { link: '/dashboard/reports', label: formatMessage(messages.activeReports), icon: ReportIcon }, + { + link: '/dashboard/notifications', + label: formatMessage(messages.notifications), + icon: NotificationsIcon, + }, + { + link: '/dashboard/reports', + label: formatMessage(messages.activeReports), + icon: ReportIcon, + }, { link: '/dashboard/collections', label: formatMessage(commonMessages.collectionsLabel), @@ -14,15 +22,28 @@ }, { type: 'heading', label: formatMessage(messages.creators) }, { link: '/dashboard/projects', label: formatMessage(messages.projects), icon: ListIcon }, - { link: '/dashboard/organizations', label: formatMessage(messages.organizations), icon: OrganizationIcon }, - { link: '/dashboard/analytics', label: formatMessage(messages.analytics), icon: ChartIcon }, + { + link: '/dashboard/organizations', + label: formatMessage(messages.organizations), + icon: OrganizationIcon, + }, + { + link: '/dashboard/analytics', + label: formatMessage(messages.analytics), + icon: ChartIcon, + }, { link: '/dashboard/affiliate-links', label: formatMessage(commonMessages.affiliateLinksButton), icon: AffiliateIcon, shown: !!isAffiliate, }, - { link: '/dashboard/revenue', label: formatMessage(messages.revenue), icon: CurrencyIcon, matchNested: true }, + { + link: '/dashboard/revenue', + label: formatMessage(messages.revenue), + icon: CurrencyIcon, + matchNested: true, + }, ]" />
@@ -43,7 +64,7 @@ import { OrganizationIcon, ReportIcon, } from '@modrinth/assets' -import { defineMessages, commonMessages, useVIntl } from '@modrinth/ui' +import { commonMessages, defineMessages, useVIntl } from '@modrinth/ui' import { type User, UserBadge } from '@modrinth/utils' import NavStack from '~/components/ui/NavStack.vue' diff --git a/apps/frontend/src/pages/dashboard/collections.vue b/apps/frontend/src/pages/dashboard/collections.vue index 16026b767d..7569701ce9 100644 --- a/apps/frontend/src/pages/dashboard/collections.vue +++ b/apps/frontend/src/pages/dashboard/collections.vue @@ -20,7 +20,7 @@ v-slot="{ selected }" v-model="sortBy" class="!w-auto flex-grow md:flex-grow-0" - :name="formatMessage(messages.sortByControlName)" + :name="formatMessage(commonMessages.sortByLabel)" :options="['updated', 'created', 'name']" :display-name="formatCollectionSortOption" > @@ -187,10 +187,6 @@ const messages = defineMessages({ id: 'dashboard.collections.placeholder.search', defaultMessage: 'Search collections...', }, - sortByControlName: { - id: 'dashboard.collections.sort.control-name', - defaultMessage: 'Sort by', - }, sortRecentlyUpdated: { id: 'dashboard.collections.sort.recently-updated', defaultMessage: 'Recently Updated', diff --git a/apps/frontend/src/pages/dashboard/projects.vue b/apps/frontend/src/pages/dashboard/projects.vue index 93cf5ef01a..9815d01ad0 100644 --- a/apps/frontend/src/pages/dashboard/projects.vue +++ b/apps/frontend/src/pages/dashboard/projects.vue @@ -167,7 +167,7 @@
- {{ formatMessage(messages.sortByLabel) }} + {{ formatMessage(commonMessages.sortByLabel) }} Date: Tue, 24 Mar 2026 20:47:45 +0800 Subject: [PATCH 04/11] don't change the keys --- apps/frontend/src/pages/dashboard/projects.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/frontend/src/pages/dashboard/projects.vue b/apps/frontend/src/pages/dashboard/projects.vue index 9815d01ad0..f76244f91c 100644 --- a/apps/frontend/src/pages/dashboard/projects.vue +++ b/apps/frontend/src/pages/dashboard/projects.vue @@ -485,11 +485,11 @@ const user = await useUser() const projects = ref([]) const projectsWithMigrationWarning = ref([]) const selectedProjects = ref([]) -const sortBy = ref('name') +const sortBy = ref('Name') const sortOptions = computed(() => [ - { value: 'name', label: formatMessage(messages.sortOptionName) }, - { value: 'status', label: formatMessage(messages.sortOptionStatus) }, - { value: 'type', label: formatMessage(messages.sortOptionType) }, + { value: 'Name', label: formatMessage(messages.sortOptionName) }, + { value: 'Status', label: formatMessage(messages.sortOptionStatus) }, + { value: 'Type', label: formatMessage(messages.sortOptionType) }, ]) const descending = ref(false) const editLinks = reactive({ @@ -571,17 +571,17 @@ function getBulkEditDisabledTooltip(project) { function updateSort(list, sort, desc) { let sortedArray = list switch (sort) { - case 'name': + case 'Name': sortedArray = list.slice().sort((a, b) => a.title.localeCompare(b.title)) break - case 'status': + case 'Status': sortedArray = list.slice().sort((a, b) => { if (a.status < b.status) return -1 if (a.status > b.status) return 1 return 0 }) break - case 'type': + case 'Type': sortedArray = list.slice().sort((a, b) => { if (a.project_type < b.project_type) return -1 if (a.project_type > b.project_type) return 1 @@ -646,7 +646,7 @@ async function bulkEditLinks() { await initUserProjects() if (user.value?.projects) { - projects.value = updateSort(user.value.projects, 'name', false) + projects.value = updateSort(user.value.projects, 'Name', false) // minecraft_java_server type determined from component on projectV3 projects.value = projects.value.map((project) => { From 31de0f8707741084d873f716bc9de742d6f91f26 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Tue, 24 Mar 2026 20:52:59 +0800 Subject: [PATCH 05/11] undo fix --- apps/frontend/src/pages/dashboard/affiliate-links.vue | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/frontend/src/pages/dashboard/affiliate-links.vue b/apps/frontend/src/pages/dashboard/affiliate-links.vue index 9221e21c3d..a02b7fea4d 100644 --- a/apps/frontend/src/pages/dashboard/affiliate-links.vue +++ b/apps/frontend/src/pages/dashboard/affiliate-links.vue @@ -94,15 +94,12 @@ const { const filterQuery = ref('') const creatingLink = ref(false) -const currentUserId = computed(() => - auth.value?.user ? (auth.value.user as { id: string }).id : null, -) const filteredAffiliates = computed( () => affiliateLinks.value?.filter( (link: Labrinth.Affiliate.Internal.AffiliateCode) => - link.affiliate === currentUserId.value && + link.affiliate === auth.value?.user?.id && (filterQuery.value.trim() ? link.source_name.trim().toLowerCase().includes(filterQuery.value.trim().toLowerCase()) : true), From a3cc4059569263cf7bfbd70e0f67f0074794a9cd Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Wed, 1 Apr 2026 11:10:49 +0800 Subject: [PATCH 06/11] fix any err --- .../src/pages/dashboard/collections.vue | 8 +--- .../src/pages/dashboard/notifications.vue | 43 +------------------ .../frontend/src/pages/dashboard/projects.vue | 15 +------ 3 files changed, 5 insertions(+), 61 deletions(-) diff --git a/apps/frontend/src/pages/dashboard/collections.vue b/apps/frontend/src/pages/dashboard/collections.vue index 7569701ce9..56ae3783d0 100644 --- a/apps/frontend/src/pages/dashboard/collections.vue +++ b/apps/frontend/src/pages/dashboard/collections.vue @@ -42,7 +42,7 @@
@@ -245,12 +245,6 @@ const router = useNativeRouter() const validSortOptions = ['updated', 'created', 'name'] const sortBy = ref(validSortOptions.includes(route.query.s) ? route.query.s : 'updated') -const showFollowedProjectsCollection = computed(() => - formatMessage(commonMessages.followedProjectsLabel) - .toLowerCase() - .includes(filterQuery.value.toLowerCase()), -) - function formatCollectionSortOption(option) { if (option === 'updated') { return formatMessage(messages.sortRecentlyUpdated) diff --git a/apps/frontend/src/pages/dashboard/notifications.vue b/apps/frontend/src/pages/dashboard/notifications.vue index 403d7d0598..f56d216e44 100644 --- a/apps/frontend/src/pages/dashboard/notifications.vue +++ b/apps/frontend/src/pages/dashboard/notifications.vue @@ -35,7 +35,7 @@ v-if="notifTypes.length > 1" v-model="selectedType" :items="notifTypes" - :format-label="formatNotificationTypeLabel" + :format-label="(x) => (x === 'all' ? 'All' : formatProjectType(x).replace('_', ' ') + 's')" :capitalize="false" />

{{ formatMessage(messages.loadingNotifications) }}

@@ -75,6 +75,7 @@ import { Pagination, useVIntl, } from '@modrinth/ui' +import { formatProjectType } from '@modrinth/utils' import { useQuery } from '@tanstack/vue-query' import Breadcrumbs from '~/components/ui/Breadcrumbs.vue' @@ -116,30 +117,6 @@ const messages = defineMessages({ id: 'dashboard.notifications.empty.no-unread', defaultMessage: "You don't have any unread notifications.", }, - projectUpdatesType: { - id: 'dashboard.notifications.type.project-updates', - defaultMessage: 'Project updates', - }, - teamInvitesType: { - id: 'dashboard.notifications.type.team-invites', - defaultMessage: 'Team invites', - }, - organizationInvitesType: { - id: 'dashboard.notifications.type.organization-invites', - defaultMessage: 'Organization invites', - }, - statusChangesType: { - id: 'dashboard.notifications.type.status-changes', - defaultMessage: 'Status changes', - }, - moderatorMessagesType: { - id: 'dashboard.notifications.type.moderator-messages', - defaultMessage: 'Moderator messages', - }, - otherNotificationsType: { - id: 'dashboard.notifications.type.other', - defaultMessage: 'Other notifications', - }, }) const client = injectModrinthClient() @@ -158,22 +135,6 @@ const selectedType = ref('all') const page = ref(1) const perPage = ref(50) -function formatNotificationTypeLabel(type) { - if (type === 'all') { - return formatMessage(commonMessages.allProjectType) - } - - const notificationTypeMessages = { - project_update: messages.projectUpdatesType, - team_invite: messages.teamInvitesType, - organization_invite: messages.organizationInvitesType, - status_change: messages.statusChangesType, - moderator_message: messages.moderatorMessagesType, - } - - return formatMessage(notificationTypeMessages[type] ?? messages.otherNotificationsType) -} - const { data, isPending, error, refetch } = useQuery({ queryKey: computed(() => [ 'user', diff --git a/apps/frontend/src/pages/dashboard/projects.vue b/apps/frontend/src/pages/dashboard/projects.vue index f76244f91c..f1332e4ed0 100644 --- a/apps/frontend/src/pages/dashboard/projects.vue +++ b/apps/frontend/src/pages/dashboard/projects.vue @@ -249,7 +249,7 @@
- {{ formatProjectTypeLabel(project) }} + {{ formatProjectType(getProjectTypeForUrl(project.project_type, project.loaders)) }}
@@ -308,7 +308,6 @@ import { Checkbox, Combobox, commonMessages, - commonProjectTypeTitleMessages, CopyCode, defineMessages, injectNotificationManager, @@ -318,6 +317,7 @@ import { StyledInput, useVIntl, } from '@modrinth/ui' +import { formatProjectType } from '@modrinth/utils' import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue' import { getProjectTypeForUrl } from '~/helpers/projects.js' @@ -513,17 +513,6 @@ function getLinkInputPlaceholder(clearLink, isDiscord = false) { : formatMessage(messages.enterValidUrl) } -function getProjectTypeTitleMessage(type) { - return commonProjectTypeTitleMessages[type] ?? commonProjectTypeTitleMessages.project -} - -function formatProjectTypeLabel(project) { - return formatMessage( - getProjectTypeTitleMessage(getProjectTypeForUrl(project.project_type, project.loaders)), - { count: 1 }, - ) -} - function isProjectBulkEditDisabled(project) { return ( (project.permissions & EDIT_DETAILS) === EDIT_DETAILS || From 8b9f715dee3b0e5dd93a38158b92ae296f7985b0 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Wed, 1 Apr 2026 11:20:38 +0800 Subject: [PATCH 07/11] don't i18n csv --- .../src/pages/dashboard/revenue/transfers.vue | 76 +++---------------- 1 file changed, 10 insertions(+), 66 deletions(-) diff --git a/apps/frontend/src/pages/dashboard/revenue/transfers.vue b/apps/frontend/src/pages/dashboard/revenue/transfers.vue index 745ad307bc..684bd476a6 100644 --- a/apps/frontend/src/pages/dashboard/revenue/transfers.vue +++ b/apps/frontend/src/pages/dashboard/revenue/transfers.vue @@ -92,13 +92,12 @@ import { import { ButtonStyled, Combobox, - commonMessages, defineMessages, EmptyState, injectModrinthClient, useFormatDateTime, useFormatMoney, - useVIntl, + useVIntl } from '@modrinth/ui' import { useQuery } from '@tanstack/vue-query' import dayjs from 'dayjs' @@ -214,28 +213,18 @@ function transactionsToCSV() { } const newline = '\n' - const header = [ - formatMessage(messages.csvDateHeader), - formatMessage(messages.csvTypeHeader), - formatMessage(messages.csvSourceHeader), - formatMessage(messages.csvStatusHeader), - formatMessage(messages.csvAmountHeader), - formatMessage(messages.csvFeeHeader), - ].join(',') + const header = ['Date', 'Type', 'Source', 'Status', 'Amount', 'Fee'].join(',') const rows = filteredTransactions.value.map((txn) => { const date = dayjs(txn.created).format('YYYY-MM-DD HH:mm:ss') - const type = - txn.type === 'withdrawal' - ? formatMessage(messages.csvTypeWithdrawal) - : formatMessage(messages.csvTypePayout) + const type = txn.type === 'withdrawal' ? 'Withdrawal' : 'Payout' let methodOrSource = '' let status = '' let fee = '' if (txn.type === 'withdrawal') { - const method = txn.method_type || txn.method || 'unknown' + const method = txn.method_type || txn.method || 'Unknown' switch (method) { case 'paypal': methodOrSource = 'PayPal' @@ -261,20 +250,15 @@ function transactionsToCSV() { break } } - methodOrSource = formatMessage(messages.muralPayUnknown, { - unknown: formatMessage(commonMessages.unknownLabel), - }) + methodOrSource = 'Mural Pay (Unknown)' break default: - methodOrSource = - method === 'unknown' - ? formatMessage(commonMessages.unknownLabel) - : method.charAt(0).toUpperCase() + method.slice(1) + methodOrSource = method.charAt(0).toUpperCase() + method.slice(1) } status = txn.status ? txn.status.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()) - : formatMessage(commonMessages.unknownLabel) + : 'Unknown' fee = txn.fee ? Number(txn.fee).toFixed(2) : '0.00' } else { @@ -283,9 +267,9 @@ function transactionsToCSV() { .split('_') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ') - : formatMessage(commonMessages.unknownLabel) - status = formatMessage(messages.notApplicable) - fee = formatMessage(messages.notApplicable) + : 'Unknown' + status = 'N/A' + fee = 'N/A' } const amount = Number(txn.amount).toFixed(2) @@ -379,45 +363,5 @@ const messages = defineMessages({ id: 'dashboard.revenue.transactions.period.last-month', defaultMessage: 'Last month', }, - notApplicable: { - id: 'dashboard.revenue.transactions.not-applicable', - defaultMessage: 'N/A', - }, - csvDateHeader: { - id: 'dashboard.revenue.transactions.csv.header.date', - defaultMessage: 'Date', - }, - csvTypeHeader: { - id: 'dashboard.revenue.transactions.csv.header.type', - defaultMessage: 'Type', - }, - csvSourceHeader: { - id: 'dashboard.revenue.transactions.csv.header.source', - defaultMessage: 'Source', - }, - csvStatusHeader: { - id: 'dashboard.revenue.transactions.csv.header.status', - defaultMessage: 'Status', - }, - csvAmountHeader: { - id: 'dashboard.revenue.transactions.csv.header.amount', - defaultMessage: 'Amount', - }, - csvFeeHeader: { - id: 'dashboard.revenue.transactions.csv.header.fee', - defaultMessage: 'Fee', - }, - csvTypeWithdrawal: { - id: 'dashboard.revenue.transactions.csv.type.withdrawal', - defaultMessage: 'Withdrawal', - }, - csvTypePayout: { - id: 'dashboard.revenue.transactions.csv.type.payout', - defaultMessage: 'Payout', - }, - muralPayUnknown: { - id: 'dashboard.revenue.transactions.csv.source.mural-pay-unknown', - defaultMessage: 'Mural Pay ({unknown})', - }, }) From 0fb0477cb2c316e4ca7a941ef9105162fdbed7e5 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Wed, 1 Apr 2026 11:39:34 +0800 Subject: [PATCH 08/11] prepr:frontend --- apps/frontend/src/locales/en-US/index.json | 48 ------------------- .../src/pages/dashboard/revenue/transfers.vue | 2 +- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index 6d07fb2a77..bc64675ab5 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -905,24 +905,6 @@ "dashboard.notifications.loading": { "message": "Loading notifications..." }, - "dashboard.notifications.type.moderator-messages": { - "message": "Moderator messages" - }, - "dashboard.notifications.type.organization-invites": { - "message": "Organization invites" - }, - "dashboard.notifications.type.other": { - "message": "Other notifications" - }, - "dashboard.notifications.type.project-updates": { - "message": "Project updates" - }, - "dashboard.notifications.type.status-changes": { - "message": "Status changes" - }, - "dashboard.notifications.type.team-invites": { - "message": "Team invites" - }, "dashboard.organizations.button.create": { "message": "Create organization" }, @@ -1088,33 +1070,6 @@ "dashboard.revenue.transactions.btn.download-csv": { "message": "Download as CSV" }, - "dashboard.revenue.transactions.csv.header.amount": { - "message": "Amount" - }, - "dashboard.revenue.transactions.csv.header.date": { - "message": "Date" - }, - "dashboard.revenue.transactions.csv.header.fee": { - "message": "Fee" - }, - "dashboard.revenue.transactions.csv.header.source": { - "message": "Source" - }, - "dashboard.revenue.transactions.csv.header.status": { - "message": "Status" - }, - "dashboard.revenue.transactions.csv.header.type": { - "message": "Type" - }, - "dashboard.revenue.transactions.csv.source.mural-pay-unknown": { - "message": "Mural Pay ({unknown})" - }, - "dashboard.revenue.transactions.csv.type.payout": { - "message": "Payout" - }, - "dashboard.revenue.transactions.csv.type.withdrawal": { - "message": "Withdrawal" - }, "dashboard.revenue.transactions.head-title": { "message": "Transaction history" }, @@ -1127,9 +1082,6 @@ "dashboard.revenue.transactions.none.desc": { "message": "Your payouts and withdrawals will appear here." }, - "dashboard.revenue.transactions.not-applicable": { - "message": "N/A" - }, "dashboard.revenue.transactions.period.last-month": { "message": "Last month" }, diff --git a/apps/frontend/src/pages/dashboard/revenue/transfers.vue b/apps/frontend/src/pages/dashboard/revenue/transfers.vue index 684bd476a6..978e44b342 100644 --- a/apps/frontend/src/pages/dashboard/revenue/transfers.vue +++ b/apps/frontend/src/pages/dashboard/revenue/transfers.vue @@ -97,7 +97,7 @@ import { injectModrinthClient, useFormatDateTime, useFormatMoney, - useVIntl + useVIntl, } from '@modrinth/ui' import { useQuery } from '@tanstack/vue-query' import dayjs from 'dayjs' From a265e2f8979e5cf4aac91481742f740ec90d78c1 Mon Sep 17 00:00:00 2001 From: xinyihl <1012737146@qq.com> Date: Wed, 1 Apr 2026 11:43:35 +0800 Subject: [PATCH 09/11] fix: do not use button key --- apps/frontend/src/pages/dashboard/analytics.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/pages/dashboard/analytics.vue b/apps/frontend/src/pages/dashboard/analytics.vue index 954b9ac32f..96f1865561 100644 --- a/apps/frontend/src/pages/dashboard/analytics.vue +++ b/apps/frontend/src/pages/dashboard/analytics.vue @@ -12,7 +12,7 @@