Tighten issue route typing
- Remove the remaining Oxlint warnings in the issues route UI - Make promise handling explicit in navigation and refresh paths - Keep the issue snapshot shape aligned with the fields the UI reads
This commit is contained in:
parent
ce1fb16b76
commit
fb29e0179e
4 changed files with 38 additions and 18 deletions
|
|
@ -2,10 +2,34 @@ export type IssueStatus = 'open' | 'in_progress' | 'resolved' | 'dismissed';
|
||||||
export type IssueEntityType = 'track' | 'album' | 'artist';
|
export type IssueEntityType = 'track' | 'album' | 'artist';
|
||||||
export type IssuePriority = 'low' | 'normal' | 'high';
|
export type IssuePriority = 'low' | 'normal' | 'high';
|
||||||
|
|
||||||
|
export interface IssueTrackRow extends Record<string, unknown> {
|
||||||
|
bitrate?: string | number;
|
||||||
|
disc_number?: string | number;
|
||||||
|
duration?: string | number;
|
||||||
|
format?: string;
|
||||||
|
id?: string | number;
|
||||||
|
title?: string;
|
||||||
|
track_number?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IssueSnapshot {
|
export interface IssueSnapshot {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
title?: string;
|
album_track_count?: string | number;
|
||||||
|
bitrate?: string | number;
|
||||||
|
bpm?: string | number;
|
||||||
|
disc_number?: string | number;
|
||||||
|
duration?: string | number;
|
||||||
|
file_path?: string;
|
||||||
|
format?: string;
|
||||||
|
genres?: string[];
|
||||||
|
label?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
record_type?: string;
|
||||||
|
title?: string;
|
||||||
|
track_count?: string | number;
|
||||||
|
tracks?: IssueTrackRow[];
|
||||||
|
track_number?: string | number;
|
||||||
|
year?: string | number;
|
||||||
artist_name?: string;
|
artist_name?: string;
|
||||||
album_title?: string;
|
album_title?: string;
|
||||||
thumb_url?: string;
|
thumb_url?: string;
|
||||||
|
|
@ -16,14 +40,7 @@ export interface IssueSnapshot {
|
||||||
spotify_track_id?: string;
|
spotify_track_id?: string;
|
||||||
artist_id?: string | number;
|
artist_id?: string | number;
|
||||||
album_id?: string | number;
|
album_id?: string | number;
|
||||||
track_number?: string | number;
|
|
||||||
duration?: string | number;
|
|
||||||
format?: string;
|
|
||||||
bitrate?: string | number;
|
|
||||||
bpm?: string | number;
|
|
||||||
quality?: string;
|
quality?: string;
|
||||||
file_path?: string;
|
|
||||||
tracks?: Array<Record<string, unknown>>;
|
|
||||||
artist_musicbrainz_id?: string;
|
artist_musicbrainz_id?: string;
|
||||||
musicbrainz_release_id?: string;
|
musicbrainz_release_id?: string;
|
||||||
musicbrainz_recording_id?: string;
|
musicbrainz_recording_id?: string;
|
||||||
|
|
@ -44,8 +61,8 @@ export interface IssueRecord {
|
||||||
category: string;
|
category: string;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
status: IssueStatus | string;
|
status: string;
|
||||||
priority: 'low' | 'normal' | 'high' | string;
|
priority: string;
|
||||||
snapshot_data: IssueSnapshot | string | null;
|
snapshot_data: IssueSnapshot | string | null;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
launchAlbumWishlistWorkflow,
|
launchAlbumWishlistWorkflow,
|
||||||
} from '@/platform/workflows/album-workflows';
|
} from '@/platform/workflows/album-workflows';
|
||||||
|
|
||||||
import type { IssueRecord } from '../-issues.types';
|
import type { IssueRecord, IssueTrackRow } from '../-issues.types';
|
||||||
|
|
||||||
import { deleteIssue, issueDetailQueryOptions, updateIssue } from '../-issues.api';
|
import { deleteIssue, issueDetailQueryOptions, updateIssue } from '../-issues.api';
|
||||||
import {
|
import {
|
||||||
|
|
@ -439,7 +439,7 @@ export function IssueDetailModal({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTrackListing(trackRows: Array<Record<string, unknown>>) {
|
function renderTrackListing(trackRows: IssueTrackRow[]) {
|
||||||
const nodes: ReactNode[] = [];
|
const nodes: ReactNode[] = [];
|
||||||
let lastDisc: number | null = null;
|
let lastDisc: number | null = null;
|
||||||
const hasMultiDisc = trackRows.some((track) => Number(track.disc_number || 1) > 1);
|
const hasMultiDisc = trackRows.some((track) => Number(track.disc_number || 1) > 1);
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,10 @@ function getReportIssueFormError(errors: Array<unknown>): string {
|
||||||
if (!error) return '';
|
if (!error) return '';
|
||||||
if (typeof error === 'string') return error;
|
if (typeof error === 'string') return error;
|
||||||
if (error instanceof Error) return error.message;
|
if (error instanceof Error) return error.message;
|
||||||
return String(error);
|
if (typeof error === 'number' || typeof error === 'boolean' || typeof error === 'bigint') {
|
||||||
|
return String(error);
|
||||||
|
}
|
||||||
|
return 'Unable to submit this issue';
|
||||||
}
|
}
|
||||||
|
|
||||||
function notify(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info') {
|
function notify(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info') {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export function IssuesPage() {
|
||||||
const params = Route.useSearch();
|
const params = Route.useSearch();
|
||||||
|
|
||||||
const clearIssueSelection = () => {
|
const clearIssueSelection = () => {
|
||||||
navigate({
|
void navigate({
|
||||||
to: Route.fullPath,
|
to: Route.fullPath,
|
||||||
search: (prev) => normalizeIssuesSearch({ ...prev, issueId: undefined }),
|
search: (prev) => normalizeIssuesSearch({ ...prev, issueId: undefined }),
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
@ -63,7 +63,7 @@ function IssueBoard() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['issues'] });
|
void queryClient.invalidateQueries({ queryKey: ['issues'] });
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener(REFRESH_EVENT, handleRefresh);
|
window.addEventListener(REFRESH_EVENT, handleRefresh);
|
||||||
|
|
@ -80,14 +80,14 @@ function IssueBoard() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const openIssue = (issueId: number) => {
|
const openIssue = (issueId: number) => {
|
||||||
navigate({
|
void navigate({
|
||||||
to: Route.fullPath,
|
to: Route.fullPath,
|
||||||
search: (prev) => normalizeIssuesSearch({ ...prev, issueId }),
|
search: (prev) => normalizeIssuesSearch({ ...prev, issueId }),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCategoryChange = (category: string) => {
|
const onCategoryChange = (category: string) => {
|
||||||
navigate({
|
void navigate({
|
||||||
to: Route.fullPath,
|
to: Route.fullPath,
|
||||||
search: (prev) =>
|
search: (prev) =>
|
||||||
normalizeIssuesSearch({
|
normalizeIssuesSearch({
|
||||||
|
|
@ -99,7 +99,7 @@ function IssueBoard() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onStatusChange = (status: IssueStatus | 'all') => {
|
const onStatusChange = (status: IssueStatus | 'all') => {
|
||||||
navigate({
|
void navigate({
|
||||||
to: Route.fullPath,
|
to: Route.fullPath,
|
||||||
search: (prev) =>
|
search: (prev) =>
|
||||||
normalizeIssuesSearch({
|
normalizeIssuesSearch({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue