chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports) - Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier - Add root scripts: format, format:check, lint, lint:fix - Reformat 257 files across admin-web, ops-web, desktop-client, packages - Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters - Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex - Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,63 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import { QuestionCircleOutlined, UserOutlined } from "@ant-design/icons-vue";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
import type { MediaPlatform } from "@geo/shared-types";
|
||||
import { computed, ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { QuestionCircleOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import type { MediaPlatform } from '@geo/shared-types'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { articlesApi, mediaApi } from "@/lib/api";
|
||||
import { getPublishStatusMeta } from "@/lib/display";
|
||||
import { getPublishPlatformMeta, normalizePublishPlatformId } from "@/lib/publish-platforms";
|
||||
import { articlesApi, mediaApi } from '@/lib/api'
|
||||
import { getPublishStatusMeta } from '@/lib/display'
|
||||
import { getPublishPlatformMeta, normalizePublishPlatformId } from '@/lib/publish-platforms'
|
||||
|
||||
interface PublishPlatformEntry {
|
||||
key: string;
|
||||
name: string;
|
||||
shortName: string;
|
||||
accent: string;
|
||||
accountName: string;
|
||||
avatarUrl: string;
|
||||
status: PublishRecordStatus;
|
||||
key: string
|
||||
name: string
|
||||
shortName: string
|
||||
accent: string
|
||||
accountName: string
|
||||
avatarUrl: string
|
||||
status: PublishRecordStatus
|
||||
}
|
||||
|
||||
type PublishRecordStatus = "success" | "failed" | "publishing";
|
||||
type PublishRecordStatus = 'success' | 'failed' | 'publishing'
|
||||
|
||||
interface PublishPlatformSection {
|
||||
key: PublishRecordStatus;
|
||||
label: string;
|
||||
tone: "success" | "failed" | "pending";
|
||||
entries: PublishPlatformEntry[];
|
||||
key: PublishRecordStatus
|
||||
label: string
|
||||
tone: 'success' | 'failed' | 'pending'
|
||||
entries: PublishPlatformEntry[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
status?: string | null;
|
||||
articleId?: number | null;
|
||||
showPlatforms?: boolean;
|
||||
}>(), {
|
||||
status: null,
|
||||
articleId: null,
|
||||
showPlatforms: true,
|
||||
});
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
status?: string | null
|
||||
articleId?: number | null
|
||||
showPlatforms?: boolean
|
||||
}>(),
|
||||
{
|
||||
status: null,
|
||||
articleId: null,
|
||||
showPlatforms: true,
|
||||
},
|
||||
)
|
||||
|
||||
const { t } = useI18n();
|
||||
const popoverOpen = ref(false);
|
||||
const shouldLoadRecords = computed(() => Boolean(props.articleId) && props.status !== "unpublished");
|
||||
const shouldLoadPlatforms = computed(() => props.showPlatforms && shouldLoadRecords.value);
|
||||
const { t } = useI18n()
|
||||
const popoverOpen = ref(false)
|
||||
const shouldLoadRecords = computed(() => Boolean(props.articleId) && props.status !== 'unpublished')
|
||||
const shouldLoadPlatforms = computed(() => props.showPlatforms && shouldLoadRecords.value)
|
||||
|
||||
const platformsQuery = useQuery({
|
||||
queryKey: ["media", "platforms", "publish-status"],
|
||||
queryKey: ['media', 'platforms', 'publish-status'],
|
||||
enabled: computed(() => popoverOpen.value && shouldLoadPlatforms.value),
|
||||
queryFn: () => mediaApi.platforms(),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
})
|
||||
|
||||
const publishRecordsQuery = useQuery({
|
||||
queryKey: computed(() => ["articles", "publish-records", props.articleId]),
|
||||
queryKey: computed(() => ['articles', 'publish-records', props.articleId]),
|
||||
enabled: shouldLoadRecords,
|
||||
queryFn: () => articlesApi.publishRecords(props.articleId as number),
|
||||
staleTime: 30 * 1000,
|
||||
});
|
||||
})
|
||||
|
||||
const hasPopover = computed(() => props.showPlatforms && shouldLoadRecords.value);
|
||||
const hasPopover = computed(() => props.showPlatforms && shouldLoadRecords.value)
|
||||
|
||||
const platformMap = computed(() => {
|
||||
return new Map(
|
||||
@@ -65,166 +68,166 @@ const platformMap = computed(() => {
|
||||
normalizePublishPlatformId(platform.platform_id),
|
||||
platform,
|
||||
]),
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
const platformEntries = computed<PublishPlatformEntry[]>(() => {
|
||||
const seen = new Set<string>();
|
||||
const entries: PublishPlatformEntry[] = [];
|
||||
const seen = new Set<string>()
|
||||
const entries: PublishPlatformEntry[] = []
|
||||
|
||||
for (const record of publishRecordsQuery.data.value ?? []) {
|
||||
const key = `${record.platform_id}:${record.platform_account_id}`;
|
||||
const key = `${record.platform_id}:${record.platform_account_id}`
|
||||
if (seen.has(key)) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
seen.add(key);
|
||||
seen.add(key)
|
||||
|
||||
const normalizedId = normalizePublishPlatformId(record.platform_id);
|
||||
const platform = platformMap.value.get(normalizedId);
|
||||
const fallback = getPublishPlatformMeta(normalizedId);
|
||||
const normalizedId = normalizePublishPlatformId(record.platform_id)
|
||||
const platform = platformMap.value.get(normalizedId)
|
||||
const fallback = getPublishPlatformMeta(normalizedId)
|
||||
|
||||
entries.push({
|
||||
key,
|
||||
name: record.platform_name || platform?.name || fallback.name,
|
||||
shortName: platform?.short_name || fallback.shortName,
|
||||
accent: platform?.accent_color || fallback.accent,
|
||||
accountName: record.platform_nickname || "--",
|
||||
avatarUrl: "",
|
||||
accountName: record.platform_nickname || '--',
|
||||
avatarUrl: '',
|
||||
status: normalizePublishRecordStatus(record.status),
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return entries;
|
||||
});
|
||||
return entries
|
||||
})
|
||||
|
||||
const successEntries = computed(() =>
|
||||
platformEntries.value.filter((entry) => entry.status === "success"),
|
||||
);
|
||||
platformEntries.value.filter((entry) => entry.status === 'success'),
|
||||
)
|
||||
|
||||
const failedEntries = computed(() =>
|
||||
platformEntries.value.filter((entry) => entry.status === "failed"),
|
||||
);
|
||||
platformEntries.value.filter((entry) => entry.status === 'failed'),
|
||||
)
|
||||
|
||||
const pendingEntries = computed(() =>
|
||||
platformEntries.value.filter((entry) => entry.status === "publishing"),
|
||||
);
|
||||
platformEntries.value.filter((entry) => entry.status === 'publishing'),
|
||||
)
|
||||
|
||||
const displayStatus = computed(() => {
|
||||
if (successEntries.value.length > 0 && failedEntries.value.length > 0) {
|
||||
return "partial_success";
|
||||
return 'partial_success'
|
||||
}
|
||||
|
||||
if (failedEntries.value.length > 0) {
|
||||
return "failed";
|
||||
return 'failed'
|
||||
}
|
||||
|
||||
if (pendingEntries.value.length > 0) {
|
||||
return "publishing";
|
||||
return 'publishing'
|
||||
}
|
||||
|
||||
if (successEntries.value.length > 0) {
|
||||
return "success";
|
||||
return 'success'
|
||||
}
|
||||
|
||||
if (props.status === "partial_success") {
|
||||
return "partial_success";
|
||||
if (props.status === 'partial_success') {
|
||||
return 'partial_success'
|
||||
}
|
||||
|
||||
if (props.status === "publish_success" || props.status === "published") {
|
||||
return "success";
|
||||
if (props.status === 'publish_success' || props.status === 'published') {
|
||||
return 'success'
|
||||
}
|
||||
|
||||
if (props.status === "publish_failed") {
|
||||
return "failed";
|
||||
if (props.status === 'publish_failed') {
|
||||
return 'failed'
|
||||
}
|
||||
|
||||
if (props.status === "pending_review") {
|
||||
return "failed";
|
||||
if (props.status === 'pending_review') {
|
||||
return 'failed'
|
||||
}
|
||||
|
||||
return props.status ?? null;
|
||||
});
|
||||
return props.status ?? null
|
||||
})
|
||||
|
||||
const statusMeta = computed(() => getPublishStatusMeta(displayStatus.value));
|
||||
const statusMeta = computed(() => getPublishStatusMeta(displayStatus.value))
|
||||
|
||||
const statusTone = computed(() => {
|
||||
switch (displayStatus.value) {
|
||||
case "success":
|
||||
return "success";
|
||||
case "failed":
|
||||
return "failed";
|
||||
case "publishing":
|
||||
return "processing";
|
||||
case "partial_success":
|
||||
return "warning";
|
||||
case 'success':
|
||||
return 'success'
|
||||
case 'failed':
|
||||
return 'failed'
|
||||
case 'publishing':
|
||||
return 'processing'
|
||||
case 'partial_success':
|
||||
return 'warning'
|
||||
default:
|
||||
return "default";
|
||||
return 'default'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const popoverSections = computed<PublishPlatformSection[]>(() => {
|
||||
const sections: PublishPlatformSection[] = [];
|
||||
const sections: PublishPlatformSection[] = []
|
||||
|
||||
if (failedEntries.value.length > 0) {
|
||||
sections.push({
|
||||
key: "failed",
|
||||
label: `${getPublishStatusMeta("failed").label}:`,
|
||||
tone: "failed",
|
||||
key: 'failed',
|
||||
label: `${getPublishStatusMeta('failed').label}:`,
|
||||
tone: 'failed',
|
||||
entries: failedEntries.value,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (pendingEntries.value.length > 0) {
|
||||
sections.push({
|
||||
key: "publishing",
|
||||
label: `${getPublishStatusMeta("publishing").label}:`,
|
||||
tone: "pending",
|
||||
key: 'publishing',
|
||||
label: `${getPublishStatusMeta('publishing').label}:`,
|
||||
tone: 'pending',
|
||||
entries: pendingEntries.value,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (successEntries.value.length > 0) {
|
||||
sections.push({
|
||||
key: "success",
|
||||
label: `${getPublishStatusMeta("success").label}:`,
|
||||
tone: "success",
|
||||
key: 'success',
|
||||
label: `${getPublishStatusMeta('success').label}:`,
|
||||
tone: 'success',
|
||||
entries: successEntries.value,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return sections;
|
||||
});
|
||||
return sections
|
||||
})
|
||||
|
||||
function handlePopoverChange(nextOpen: boolean): void {
|
||||
popoverOpen.value = nextOpen;
|
||||
popoverOpen.value = nextOpen
|
||||
}
|
||||
|
||||
function normalizePublishRecordStatus(status?: string | null): PublishRecordStatus {
|
||||
switch (String(status ?? "").trim()) {
|
||||
case "success":
|
||||
case "published":
|
||||
case "publish_success":
|
||||
return "success";
|
||||
case "publishing":
|
||||
case "running":
|
||||
case "queued":
|
||||
return "publishing";
|
||||
case "pending_review":
|
||||
return "failed";
|
||||
case "failed":
|
||||
case "publish_failed":
|
||||
switch (String(status ?? '').trim()) {
|
||||
case 'success':
|
||||
case 'published':
|
||||
case 'publish_success':
|
||||
return 'success'
|
||||
case 'publishing':
|
||||
case 'running':
|
||||
case 'queued':
|
||||
return 'publishing'
|
||||
case 'pending_review':
|
||||
return 'failed'
|
||||
case 'failed':
|
||||
case 'publish_failed':
|
||||
default:
|
||||
return "failed";
|
||||
return 'failed'
|
||||
}
|
||||
}
|
||||
|
||||
function getAccountInitial(accountName: string): string {
|
||||
const trimmed = String(accountName ?? "").trim();
|
||||
if (!trimmed || trimmed === "--") {
|
||||
return "";
|
||||
const trimmed = String(accountName ?? '').trim()
|
||||
if (!trimmed || trimmed === '--') {
|
||||
return ''
|
||||
}
|
||||
|
||||
return trimmed.slice(0, 1).toUpperCase();
|
||||
return trimmed.slice(0, 1).toUpperCase()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -243,7 +246,7 @@ function getAccountInitial(accountName: string): string {
|
||||
class="article-publish-status__loading"
|
||||
>
|
||||
<a-spin size="small" />
|
||||
<span>{{ t("common.loading") }}</span>
|
||||
<span>{{ t('common.loading') }}</span>
|
||||
</div>
|
||||
|
||||
<div v-else-if="popoverSections.length" class="article-publish-status__sections">
|
||||
@@ -282,7 +285,9 @@ function getAccountInitial(accountName: string): string {
|
||||
</template>
|
||||
<UserOutlined v-else />
|
||||
</span>
|
||||
<span class="article-publish-status__account-name">{{ entry.accountName }}</span>
|
||||
<span class="article-publish-status__account-name">
|
||||
{{ entry.accountName }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -304,7 +309,7 @@ function getAccountInitial(accountName: string): string {
|
||||
</div>
|
||||
|
||||
<span v-else class="article-publish-status__empty">
|
||||
{{ t("media.records.empty") }}
|
||||
{{ t('media.records.empty') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -347,7 +352,10 @@ function getAccountInitial(accountName: string): string {
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.article-publish-status__badge--success {
|
||||
@@ -382,7 +390,7 @@ function getAccountInitial(accountName: string): string {
|
||||
|
||||
.article-publish-status__badge-dot {
|
||||
width: 6px;
|
||||
height:6px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user