Format & lint React code
This commit is contained in:
parent
018a554f35
commit
a2495aaba7
10 changed files with 94 additions and 83 deletions
|
|
@ -82,5 +82,12 @@ The recommended dev flow keeps the backend and frontend separate:
|
||||||
```
|
```
|
||||||
Vite hot reloads the React side when you change webui files.
|
Vite hot reloads the React side when you change webui files.
|
||||||
|
|
||||||
|
For linting and formatting, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run check
|
||||||
|
npm run fix
|
||||||
|
```
|
||||||
|
|
||||||
If you want a convenience wrapper, the repo root also includes `./dev.sh`.
|
If you want a convenience wrapper, the repo root also includes `./dev.sh`.
|
||||||
It starts both halves together and is most useful on Linux, macOS, and WSL.
|
It starts both halves together and is most useful on Linux, macOS, and WSL.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"check": "vp check src",
|
||||||
"dev": "vp dev",
|
"dev": "vp dev",
|
||||||
|
"fix": "vp check --fix src",
|
||||||
"build": "vp build",
|
"build": "vp build",
|
||||||
"test": "vp test run",
|
"test": "vp test run",
|
||||||
"test:watch": "vp test",
|
"test:watch": "vp test",
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,11 @@ function FormDemo() {
|
||||||
<FormField label="Priority" helperText="Set urgency">
|
<FormField label="Priority" helperText="Set urgency">
|
||||||
<OptionButtonGroup>
|
<OptionButtonGroup>
|
||||||
{(['low', 'normal', 'high'] as const).map((value) => (
|
{(['low', 'normal', 'high'] as const).map((value) => (
|
||||||
<OptionButton key={value} onClick={() => setPriority(value)} selected={priority === value}>
|
<OptionButton
|
||||||
|
key={value}
|
||||||
|
onClick={() => setPriority(value)}
|
||||||
|
selected={priority === value}
|
||||||
|
>
|
||||||
{value[0].toUpperCase()}
|
{value[0].toUpperCase()}
|
||||||
{value.slice(1)}
|
{value.slice(1)}
|
||||||
</OptionButton>
|
</OptionButton>
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,7 @@ export function OptionCardGroup({
|
||||||
return <div className={mergeClassNames(styles.optionCardGroup, className)}>{children}</div>;
|
return <div className={mergeClassNames(styles.optionCardGroup, className)}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptionCardProps
|
export interface OptionCardProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title'> {
|
||||||
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title'> {
|
|
||||||
description?: ReactNode;
|
description?: ReactNode;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
|
|
@ -163,7 +162,14 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
||||||
{ className, type = 'button', ...props },
|
{ className, type = 'button', ...props },
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
return <button ref={ref} className={mergeClassNames(styles.button, className)} type={type} {...props} />;
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className={mergeClassNames(styles.button, className)}
|
||||||
|
type={type}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export function FormError({ className, message }: { className?: string; message?: ReactNode }) {
|
export function FormError({ className, message }: { className?: string; message?: ReactNode }) {
|
||||||
|
|
@ -176,12 +182,6 @@ export function FormError({ className, message }: { className?: string; message?
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FormActions({
|
export function FormActions({ className, children }: { children: ReactNode; className?: string }) {
|
||||||
className,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}) {
|
|
||||||
return <div className={mergeClassNames(styles.formActions, className)}>{children}</div>;
|
return <div className={mergeClassNames(styles.formActions, className)}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
webui/src/platform/shell/globals.d.ts
vendored
7
webui/src/platform/shell/globals.d.ts
vendored
|
|
@ -1,18 +1,17 @@
|
||||||
import type { ShellProfileContext, ShellRouteDefinition, ShellPageId } from './bridge';
|
|
||||||
import type {
|
import type {
|
||||||
DownloadMissingAlbumWorkflowInput,
|
DownloadMissingAlbumWorkflowInput,
|
||||||
WishlistAlbumWorkflowInput,
|
WishlistAlbumWorkflowInput,
|
||||||
} from '@/platform/workflows/album-workflows';
|
} from '@/platform/workflows/album-workflows';
|
||||||
import type { IssueDomainBridge } from '@/routes/issues/-issues.types';
|
import type { IssueDomainBridge } from '@/routes/issues/-issues.types';
|
||||||
|
|
||||||
|
import type { ShellProfileContext, ShellRouteDefinition, ShellPageId } from './bridge';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
showToast?: (message: string, type?: string, durationOrContext?: number | string) => void;
|
showToast?: (message: string, type?: string, durationOrContext?: number | string) => void;
|
||||||
SoulSyncIssueDomain?: IssueDomainBridge;
|
SoulSyncIssueDomain?: IssueDomainBridge;
|
||||||
SoulSyncWorkflowActions?: {
|
SoulSyncWorkflowActions?: {
|
||||||
openDownloadMissingAlbum: (
|
openDownloadMissingAlbum: (input: DownloadMissingAlbumWorkflowInput) => void | Promise<void>;
|
||||||
input: DownloadMissingAlbumWorkflowInput,
|
|
||||||
) => void | Promise<void>;
|
|
||||||
openAddToWishlistAlbum: (input: WishlistAlbumWorkflowInput) => void | Promise<void>;
|
openAddToWishlistAlbum: (input: WishlistAlbumWorkflowInput) => void | Promise<void>;
|
||||||
notify?: (message: string, type?: string) => void;
|
notify?: (message: string, type?: string) => void;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,9 @@ describe('issues route', () => {
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(
|
expect(
|
||||||
fetchMock.mock.calls.some(([request]) => request instanceof Request && request.method === 'POST'),
|
fetchMock.mock.calls.some(
|
||||||
|
([request]) => request instanceof Request && request.method === 'POST',
|
||||||
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { Button, FormField, TextArea } from '@/components/form';
|
||||||
import {
|
import {
|
||||||
launchAlbumDownloadWorkflow,
|
launchAlbumDownloadWorkflow,
|
||||||
launchAlbumWishlistWorkflow,
|
launchAlbumWishlistWorkflow,
|
||||||
} from '@/platform/workflows/album-workflows';
|
} from '@/platform/workflows/album-workflows';
|
||||||
import { Button, FormField, TextArea } from '@/components/form';
|
|
||||||
|
|
||||||
import type { IssueRecord } from '../-issues.types';
|
import type { IssueRecord } from '../-issues.types';
|
||||||
|
|
||||||
|
|
@ -429,7 +429,9 @@ export function IssueDetailModal({
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.issueDetailTracklistDur}>{duration}</span>
|
<span className={styles.issueDetailTracklistDur}>{duration}</span>
|
||||||
<span className={styles.issueDetailTracklistMeta}>
|
<span className={styles.issueDetailTracklistMeta}>
|
||||||
{format ? <span className={styles.issueTrackBadge}>{format}</span> : null}
|
{format ? (
|
||||||
|
<span className={styles.issueTrackBadge}>{format}</span>
|
||||||
|
) : null}
|
||||||
{bitrate ? (
|
{bitrate ? (
|
||||||
<span className={styles.issueTrackBadge}>{bitrate}</span>
|
<span className={styles.issueTrackBadge}>{bitrate}</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -442,27 +444,27 @@ export function IssueDetailModal({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{issue.entity_type !== 'artist' && isAdmin && (
|
{issue.entity_type !== 'artist' && isAdmin && (
|
||||||
<div className={styles.issueDetailSection}>
|
<div className={styles.issueDetailSection}>
|
||||||
<div className={styles.issueDetailSectionTitle}>Admin Actions</div>
|
<div className={styles.issueDetailSectionTitle}>Admin Actions</div>
|
||||||
<div className={styles.issueActionButtons}>
|
<div className={styles.issueActionButtons}>
|
||||||
<Button
|
<Button
|
||||||
className={styles.issueActionDownload}
|
className={styles.issueActionDownload}
|
||||||
type="button"
|
type="button"
|
||||||
disabled={downloadWorkflowMutation.isPending}
|
disabled={downloadWorkflowMutation.isPending}
|
||||||
onClick={() => downloadWorkflowMutation.mutate(albumWorkflowInput)}
|
onClick={() => downloadWorkflowMutation.mutate(albumWorkflowInput)}
|
||||||
>
|
>
|
||||||
{downloadWorkflowMutation.isPending ? 'Loading...' : 'Download Album'}
|
{downloadWorkflowMutation.isPending ? 'Loading...' : 'Download Album'}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className={styles.issueActionWishlist}
|
className={styles.issueActionWishlist}
|
||||||
type="button"
|
type="button"
|
||||||
disabled={wishlistWorkflowMutation.isPending}
|
disabled={wishlistWorkflowMutation.isPending}
|
||||||
onClick={() => wishlistWorkflowMutation.mutate(albumWorkflowInput)}
|
onClick={() => wishlistWorkflowMutation.mutate(albumWorkflowInput)}
|
||||||
>
|
>
|
||||||
{wishlistWorkflowMutation.isPending ? 'Loading...' : 'Add to Wishlist'}
|
{wishlistWorkflowMutation.isPending ? 'Loading...' : 'Add to Wishlist'}
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
|
|
@ -494,11 +496,7 @@ export function IssueDetailModal({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.modalFooter}>
|
<div className={styles.modalFooter}>
|
||||||
<Button
|
<Button className={styles.modalButtonSecondary} type="button" onClick={onClose}>
|
||||||
className={styles.modalButtonSecondary}
|
|
||||||
type="button"
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
{!isLoading && !error && issue && (
|
{!isLoading && !error && issue && (
|
||||||
|
|
@ -626,8 +624,10 @@ function getTrackMetaItems(snapshot: ReturnType<typeof parseSnapshot>) {
|
||||||
const duration = formatDuration(snapshot.duration);
|
const duration = formatDuration(snapshot.duration);
|
||||||
if (duration) items.push({ icon: 'T', label: 'Duration', value: duration });
|
if (duration) items.push({ icon: 'T', label: 'Duration', value: duration });
|
||||||
if (snapshot.format) items.push({ icon: 'F', label: 'Format', value: String(snapshot.format) });
|
if (snapshot.format) items.push({ icon: 'F', label: 'Format', value: String(snapshot.format) });
|
||||||
if (snapshot.bitrate) items.push({ icon: 'B', label: 'Bitrate', value: `${snapshot.bitrate} kbps` });
|
if (snapshot.bitrate)
|
||||||
|
items.push({ icon: 'B', label: 'Bitrate', value: `${snapshot.bitrate} kbps` });
|
||||||
if (snapshot.bpm) items.push({ icon: 'M', label: 'BPM', value: String(snapshot.bpm) });
|
if (snapshot.bpm) items.push({ icon: 'M', label: 'BPM', value: String(snapshot.bpm) });
|
||||||
if (snapshot.quality) items.push({ icon: 'Q', label: 'Quality', value: String(snapshot.quality) });
|
if (snapshot.quality)
|
||||||
|
items.push({ icon: 'Q', label: 'Quality', value: String(snapshot.quality) });
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { useForm } from "@tanstack/react-form";
|
import { useForm } from '@tanstack/react-form';
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import { getShellProfileContext } from "@/platform/shell/bridge";
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
FormActions,
|
FormActions,
|
||||||
|
|
@ -15,10 +14,11 @@ import {
|
||||||
OptionCardGroup,
|
OptionCardGroup,
|
||||||
TextArea,
|
TextArea,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@/components/form";
|
} from '@/components/form';
|
||||||
import { useShellBridge } from "@/platform/shell/route-controllers";
|
import { getShellProfileContext } from '@/platform/shell/bridge';
|
||||||
|
import { useShellBridge } from '@/platform/shell/route-controllers';
|
||||||
|
|
||||||
import type { IssuePriority, IssueReportPayload } from "../-issues.types";
|
import type { IssuePriority, IssueReportPayload } from '../-issues.types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REFRESH_EVENT,
|
REFRESH_EVENT,
|
||||||
|
|
@ -26,10 +26,10 @@ import {
|
||||||
createIssue,
|
createIssue,
|
||||||
getIssueCategoriesForEntity,
|
getIssueCategoriesForEntity,
|
||||||
issueCountsQueryOptions,
|
issueCountsQueryOptions,
|
||||||
} from "../-issues.helpers";
|
} from '../-issues.helpers';
|
||||||
import styles from "./issue-detail-modal.module.css";
|
import styles from './issue-detail-modal.module.css';
|
||||||
|
|
||||||
const ISSUE_DOMAIN_QUERY_KEY = ["issues"] as const;
|
const ISSUE_DOMAIN_QUERY_KEY = ['issues'] as const;
|
||||||
|
|
||||||
interface ReportIssueFormValues {
|
interface ReportIssueFormValues {
|
||||||
category: string;
|
category: string;
|
||||||
|
|
@ -39,10 +39,10 @@ interface ReportIssueFormValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_REPORT_ISSUE_VALUES: ReportIssueFormValues = {
|
const DEFAULT_REPORT_ISSUE_VALUES: ReportIssueFormValues = {
|
||||||
category: "",
|
category: '',
|
||||||
description: "",
|
description: '',
|
||||||
priority: "normal",
|
priority: 'normal',
|
||||||
title: "",
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function IssueDomainHost() {
|
export function IssueDomainHost() {
|
||||||
|
|
@ -127,7 +127,7 @@ function ReportIssueModal({
|
||||||
[payload.entityType],
|
[payload.entityType],
|
||||||
);
|
);
|
||||||
const entityLabel =
|
const entityLabel =
|
||||||
payload.entityType === "track" ? "Track" : payload.entityType === "album" ? "Album" : "Artist";
|
payload.entityType === 'track' ? 'Track' : payload.entityType === 'album' ? 'Album' : 'Artist';
|
||||||
|
|
||||||
const createMutation = useMutation({
|
const createMutation = useMutation({
|
||||||
mutationFn: async (values: ReportIssueFormValues) => {
|
mutationFn: async (values: ReportIssueFormValues) => {
|
||||||
|
|
@ -141,7 +141,7 @@ function ReportIssueModal({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
notify("Issue reported successfully", "success");
|
notify('Issue reported successfully', 'success');
|
||||||
onSubmitted();
|
onSubmitted();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -161,9 +161,9 @@ function ReportIssueModal({
|
||||||
await createMutation.mutateAsync(normalizedValues);
|
await createMutation.mutateAsync(normalizedValues);
|
||||||
} catch (mutationError) {
|
} catch (mutationError) {
|
||||||
const message =
|
const message =
|
||||||
mutationError instanceof Error ? mutationError.message : "Failed to submit issue";
|
mutationError instanceof Error ? mutationError.message : 'Failed to submit issue';
|
||||||
formApi.setErrorMap({ onSubmit: message });
|
formApi.setErrorMap({ onSubmit: message });
|
||||||
notify(message, "error");
|
notify(message, 'error');
|
||||||
throw mutationError;
|
throw mutationError;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -206,7 +206,7 @@ function ReportIssueModal({
|
||||||
{payload.artistName ? (
|
{payload.artistName ? (
|
||||||
<div className={styles.reportIssueEntityArtist}>
|
<div className={styles.reportIssueEntityArtist}>
|
||||||
{payload.artistName}
|
{payload.artistName}
|
||||||
{payload.albumTitle ? ` - ${payload.albumTitle}` : ""}
|
{payload.albumTitle ? ` - ${payload.albumTitle}` : ''}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -227,9 +227,9 @@ function ReportIssueModal({
|
||||||
field.handleChange(category);
|
field.handleChange(category);
|
||||||
createMutation.reset();
|
createMutation.reset();
|
||||||
form.setErrorMap({ onSubmit: undefined });
|
form.setErrorMap({ onSubmit: undefined });
|
||||||
if (!form.getFieldMeta("title")?.isTouched) {
|
if (!form.getFieldMeta('title')?.isTouched) {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
"title",
|
'title',
|
||||||
createDefaultIssueTitle(category, payload.entityName),
|
createDefaultIssueTitle(category, payload.entityName),
|
||||||
{ dontUpdateMeta: true },
|
{ dontUpdateMeta: true },
|
||||||
);
|
);
|
||||||
|
|
@ -298,7 +298,7 @@ function ReportIssueModal({
|
||||||
label="Priority"
|
label="Priority"
|
||||||
>
|
>
|
||||||
<OptionButtonGroup>
|
<OptionButtonGroup>
|
||||||
{(["low", "normal", "high"] as const).map((priority) => (
|
{(['low', 'normal', 'high'] as const).map((priority) => (
|
||||||
<OptionButton
|
<OptionButton
|
||||||
key={priority}
|
key={priority}
|
||||||
onClick={() => field.handleChange(priority)}
|
onClick={() => field.handleChange(priority)}
|
||||||
|
|
@ -326,11 +326,7 @@ function ReportIssueModal({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormActions className={styles.modalFooter}>
|
<FormActions className={styles.modalFooter}>
|
||||||
<Button
|
<Button className={styles.modalButtonSecondary} type="button" onClick={onClose}>
|
||||||
className={styles.modalButtonSecondary}
|
|
||||||
type="button"
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<form.Subscribe
|
<form.Subscribe
|
||||||
|
|
@ -348,7 +344,7 @@ function ReportIssueModal({
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!state.category || !state.title.trim() || isSubmitting}
|
disabled={!state.category || !state.title.trim() || isSubmitting}
|
||||||
>
|
>
|
||||||
{isSubmitting ? "Submitting..." : "Submit Issue"}
|
{isSubmitting ? 'Submitting...' : 'Submit Issue'}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
@ -373,27 +369,27 @@ function validateReportIssueForm(
|
||||||
values: ReportIssueFormValues,
|
values: ReportIssueFormValues,
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
const normalizedValues = normalizeReportIssueFormValues(values);
|
const normalizedValues = normalizeReportIssueFormValues(values);
|
||||||
if (!profileId) return "Profile is still loading";
|
if (!profileId) return 'Profile is still loading';
|
||||||
if (!normalizedValues.category) return "Please select an issue category";
|
if (!normalizedValues.category) return 'Please select an issue category';
|
||||||
if (!normalizedValues.title) return "Please provide a title for the issue";
|
if (!normalizedValues.title) return 'Please provide a title for the issue';
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReportIssueFormError(errors: Array<unknown>): string {
|
function getReportIssueFormError(errors: Array<unknown>): string {
|
||||||
const error = errors.find(Boolean);
|
const error = errors.find(Boolean);
|
||||||
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);
|
return String(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function notify(message: string, type: "success" | "error" | "warning" | "info" = "info") {
|
function notify(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info') {
|
||||||
window.showToast?.(message, type);
|
window.showToast?.(message, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBadge(openCount: number) {
|
function updateBadge(openCount: number) {
|
||||||
const badge = document.getElementById("issues-nav-badge");
|
const badge = document.getElementById('issues-nav-badge');
|
||||||
if (!badge) return;
|
if (!badge) return;
|
||||||
badge.textContent = String(openCount || 0);
|
badge.textContent = String(openCount || 0);
|
||||||
badge.classList.toggle("hidden", !openCount);
|
badge.classList.toggle('hidden', !openCount);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { getShellProfileContext } from '@/platform/shell/bridge';
|
|
||||||
import { Select } from '@/components/form';
|
import { Select } from '@/components/form';
|
||||||
|
import { getShellProfileContext } from '@/platform/shell/bridge';
|
||||||
import { useReactPageShell } from '@/platform/shell/route-controllers';
|
import { useReactPageShell } from '@/platform/shell/route-controllers';
|
||||||
|
|
||||||
import type { IssueCounts, IssueRecord, IssueStatus } from '../-issues.types';
|
import type { IssueCounts, IssueRecord, IssueStatus } from '../-issues.types';
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export default defineConfig({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
sortPackageJson: true,
|
sortPackageJson: true,
|
||||||
|
trailingComma: 'all',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
tanstackRouter({
|
tanstackRouter({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue