mirror of
https://github.com/willchen96/mike.git
synced 2026-07-24 23:41:04 +02:00
Merge pull request #235 from amal66/olp-pr/lint-burndown
[Lint 04] fix: burn down frontend eslint errors; make CI lint blocking
This commit is contained in:
commit
7d2ba4b87f
13 changed files with 27 additions and 12 deletions
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
|
@ -7,7 +7,7 @@
|
|||
# on a tree without those pieces the test steps no-op and the build still
|
||||
# gates the merge. Beyond the fork version this also builds the frontend
|
||||
# (placeholder NEXT_PUBLIC_* env — verified sufficient for `next build`) and
|
||||
# runs eslint as an advisory step (see the comment on that step).
|
||||
# runs eslint as a blocking gate (the error backlog is at zero).
|
||||
name: CI
|
||||
|
||||
on:
|
||||
|
|
@ -61,11 +61,9 @@ jobs:
|
|||
# harness PR merges); runs the suite once it exists.
|
||||
- run: npm test --if-present
|
||||
|
||||
# Advisory only for now: main currently carries 23 eslint errors, so a
|
||||
# blocking gate would turn every PR red on inherited debt. Flip this to
|
||||
# blocking (remove continue-on-error) once the backlog is burned down.
|
||||
# Blocking gate: the eslint error backlog was burned down in this PR
|
||||
# (0 errors; warnings do not fail the step), so any new error fails CI.
|
||||
- run: npm run lint
|
||||
continue-on-error: true
|
||||
|
||||
# Production build. NEXT_PUBLIC_* values are inlined at build time and
|
||||
# only need to be well-formed here — nothing is contacted during build.
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ export function AskInputPopup({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- auto-submit when every question is answered; submit() sets state as part of the side effect
|
||||
if (canSubmit) submit();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ export function CaseLawPanel({
|
|||
|
||||
useEffect(() => {
|
||||
if (tab.opinions?.length) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- sync path of an async fetch effect: serve prop/cache data without a loading flash
|
||||
setOpinions(tab.opinions);
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
|
|
@ -269,10 +270,12 @@ export function CaseLawPanel({
|
|||
orderOpinions(opinions).find(
|
||||
({ opinion }) => typeof opinion.opinionId === "number",
|
||||
)?.opinion.opinionId ?? null;
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset active opinion after opinions load
|
||||
setActiveOpinionId(firstOpinionId);
|
||||
}, [opinions]);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- sync quote list when the tab prop changes
|
||||
setRelevantQuotes(tab.quotes ?? []);
|
||||
}, [tab.quotes]);
|
||||
|
||||
|
|
@ -321,6 +324,7 @@ export function CaseLawPanel({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset quote selection when the quote set changes
|
||||
setQuoteIndexState({ cacheKey: quoteCacheKey, index: 0 });
|
||||
const firstQuote = relevantQuotes[0];
|
||||
setActiveQuoteKey(firstQuote ? relevantQuoteKey(firstQuote, 0) : null);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ export function ChatView({
|
|||
const panelCloseTimerRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset per-chat UI state when switching chats
|
||||
setHiddenAskInputKeys(new Set());
|
||||
}, [chatId]);
|
||||
|
||||
|
|
@ -519,6 +520,7 @@ export function ChatView({
|
|||
const c = messagesContainerRef.current;
|
||||
if (!c) return;
|
||||
c.addEventListener("scroll", updateScrollButton);
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- initial scroll-button state must be measured from the live DOM
|
||||
updateScrollButton();
|
||||
return () => c.removeEventListener("scroll", updateScrollButton);
|
||||
}, [messages, updateScrollButton]);
|
||||
|
|
@ -553,6 +555,7 @@ export function ChatView({
|
|||
useEffect(() => {
|
||||
if (messages.length === 0) {
|
||||
hasScrolledRef.current = false;
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- hide messages until scroll position is restored to avoid a visible jump
|
||||
setMessagesVisible(false);
|
||||
} else if (!hasScrolledRef.current) {
|
||||
const userMsgCount = messages.filter(
|
||||
|
|
@ -682,8 +685,8 @@ export function ChatView({
|
|||
{msg.role === "user" ? (
|
||||
<UserMessage
|
||||
content={msg.content ?? ""}
|
||||
files={(msg as any).files}
|
||||
workflow={(msg as any).workflow}
|
||||
files={msg.files}
|
||||
workflow={msg.workflow}
|
||||
/>
|
||||
) : (
|
||||
<AssistantMessage
|
||||
|
|
@ -692,11 +695,11 @@ export function ChatView({
|
|||
i === messages.length - 1 &&
|
||||
isResponseLoading
|
||||
}
|
||||
isError={!!(msg as any).error}
|
||||
isError={!!msg.error}
|
||||
errorMessage={
|
||||
typeof (msg as any)
|
||||
.error === "string"
|
||||
? (msg as any).error
|
||||
typeof msg.error ===
|
||||
"string"
|
||||
? msg.error
|
||||
: undefined
|
||||
}
|
||||
citations={msg.citations}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export function CitationQuotesHeader({
|
|||
|
||||
useEffect(() => {
|
||||
if (!hasMultipleQuotes && viewMode === "list") {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- collapse list view when quotes drop to a single item
|
||||
setViewMode("single");
|
||||
}
|
||||
}, [hasMultipleQuotes, viewMode]);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export function PreResponseWrapper({
|
|||
|
||||
useEffect(() => {
|
||||
if (forceOpen) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- streaming open/minimize latch (see comment above)
|
||||
setIsOpen(true);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export function Modal({
|
|||
// Portals can't render during SSR, so a keep-mounted modal only renders
|
||||
// (hidden) after the first client mount.
|
||||
const [hasMounted, setHasMounted] = useState(false);
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- SSR portal gate: must flip after first client mount
|
||||
useEffect(() => setHasMounted(true), []);
|
||||
const hasHeader = breadcrumbs?.length;
|
||||
const hasFooter =
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export function MfaLoginGate({ children }: { children: ReactNode }) {
|
|||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- sync fast paths of the async MFA check effect
|
||||
setGateState("idle");
|
||||
return;
|
||||
}
|
||||
|
|
@ -64,6 +65,7 @@ export function MfaLoginGate({ children }: { children: ReactNode }) {
|
|||
|
||||
if (gateState === "required" && !isVerifyPage) {
|
||||
if (hasRecentMfaVerification()) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- clear gate when a recent MFA verification exists instead of redirecting
|
||||
setGateState("verified");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ function TRResponseStatus({ isActive }: { isActive: boolean }) {
|
|||
|
||||
useEffect(() => {
|
||||
if (wasActiveRef.current && !isActive) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- timed 'Done' flash on the active->idle transition
|
||||
setShowDone(true);
|
||||
setDoneVisible(true);
|
||||
const t = setTimeout(() => setDoneVisible(false), 1500);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ export function ChatHistoryProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- clear chat state on logout inside the effect that loads chats
|
||||
setChats([]);
|
||||
setChatLimit(INITIAL_CHAT_LIMIT);
|
||||
setHasMoreChats(false);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export function useFetchDocxBytes(
|
|||
|
||||
useEffect(() => {
|
||||
if (!documentId) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- clear stale bytes when documentId is removed, within the fetch effect
|
||||
setBytes(null);
|
||||
setDownloadUrl(null);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export function useSelectedModel(): [string, (id: string) => void] {
|
|||
const [model, setModelState] = useState<string>(DEFAULT_MODEL_ID);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- hydration-safe localStorage read; SSR must render the default model
|
||||
setModelState(readStored());
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ export default function SupportPage() {
|
|||
{/* Email Display (if logged in) */}
|
||||
{user?.email && (
|
||||
<div className="text-sm text-gray-500">
|
||||
We'll respond to:{" "}
|
||||
We'll respond to:{" "}
|
||||
<span className="font-medium">
|
||||
{user.email}
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue