Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@ import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
globalIgnores([".next/**", "out/**", "build/**", "coverage/**", "next-env.d.ts"]),
{
rules: {
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" },
],
},
},
{
files: ["**/*.test.ts", "**/*.test.tsx"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-require-imports": "off",
},
},
]);

export default eslintConfig;
21 changes: 11 additions & 10 deletions src/app/(web)/tools/addeditfamily/add-edit-family.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { ToolParams, isNewRecord } from "@/lib/tool-params";
import { ToolParams } from "@/lib/tool-params";
import {
emptyHousehold,
emptyMember,
Expand Down Expand Up @@ -88,7 +88,6 @@ export function AddEditFamily({ params, initialContactId }: AddEditFamilyProps)
const [confirmCloseOpen, setConfirmCloseOpen] = useState(false);
const [addressTab, setAddressTab] = useState<"main" | "alt">("main");
const [expandedMembers, setExpandedMembers] = useState<Set<number>>(new Set());
const isNew = isNewRecord(params);

const isDirty = useMemo(
() => household !== null && JSON.stringify(household) !== originalSnapshot,
Expand All @@ -110,14 +109,6 @@ export function AddEditFamily({ params, initialContactId }: AddEditFamilyProps)
};
}, []);

useEffect(() => {
if (!defaults) return;
if (initialContactId && initialContactId > 0) {
loadHousehold(initialContactId);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialContactId, defaults]);

const loadHousehold = useCallback(async (contactId: number) => {
const result = await fetchHousehold(contactId);
if (result.success) {
Expand All @@ -129,6 +120,14 @@ export function AddEditFamily({ params, initialContactId }: AddEditFamilyProps)
}
}, []);

useEffect(() => {
if (!defaults) return;
if (initialContactId && initialContactId > 0) {
// eslint-disable-next-line react-hooks/set-state-in-effect
loadHousehold(initialContactId);
}
}, [initialContactId, defaults, loadHousehold]);

const startNewFamily = useCallback(
(lastName: string) => {
if (!defaults) return;
Expand Down Expand Up @@ -1026,6 +1025,8 @@ function AddressLine1Autocomplete({
}
if (debounceRef.current) clearTimeout(debounceRef.current);
if (value.trim().length < 3) {
// Clear predictions on short input (debounced autocomplete).
// eslint-disable-next-line react-hooks/set-state-in-effect
setPredictions([]);
return;
}
Expand Down
4 changes: 3 additions & 1 deletion src/app/(web)/tools/addresslabels/address-labels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function AddressLabels({ params }: AddressLabelsProps) {
}, []);

// Only re-fetch when filtering-relevant config changes (mode, barcode toggle),
// NOT when layout-only config changes (stock, start position, barcode format)
// NOT when layout-only config changes (stock, start position, barcode format).
const loadData = useCallback(async () => {
setIsLoading(true);
setError(null);
Expand All @@ -84,9 +84,11 @@ export function AddressLabels({ params }: AddressLabelsProps) {
} finally {
setIsLoading(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [params, config.addressMode, config.includeMissingBarcodes]);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
loadData();
}, [loadData]);

Expand Down
1 change: 1 addition & 0 deletions src/app/signin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ function SignInContent() {
}
}, []);

// eslint-disable-next-line react-hooks/preserve-manual-memoization
const startOAuth = useCallback(() => {
console.log("Redirecting to SignIn API");
setIsRedirecting(true);
Expand Down
1 change: 0 additions & 1 deletion src/components/address-labels/imb-barcode.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { View } from '@react-pdf/renderer';
import type { BarState } from '@/lib/imb-encoder';

interface ImbBarcodeProps {
/** Pre-encoded bar states string (65 chars of T/D/A/F) */
Expand Down
2 changes: 0 additions & 2 deletions src/components/address-labels/word-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
TableCell,
WidthType,
ImageRun,
AlignmentType,
BorderStyle,
convertInchesToTwip,
SectionType,
Expand All @@ -32,7 +31,6 @@ function buildLabelCell(
stock: LabelStockConfig
): TableCell {
const cellWidthTwips = convertInchesToTwip(ptToIn(stock.labelWidth));
const cellHeightTwips = convertInchesToTwip(ptToIn(stock.labelHeight));

if (!label) {
// Empty cell (for start position offset)
Expand Down
3 changes: 3 additions & 0 deletions src/components/dev-panel/dev-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export function DevPanel({ params }: DevPanelProps) {
const [isAuthorized, setIsAuthorized] = useState(false);

useEffect(() => {
// SSR hydration gate + read persisted open state from localStorage.
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true);

setIsOpen(readOpenState());
}, []);

Expand Down
3 changes: 3 additions & 0 deletions src/components/dev-panel/panels/contact-records-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ export function ContactRecordsPanel({ params, selectionRecordIds }: ContactRecor

const recordIds = hasSingleRecord ? [params.recordID!] : selectionRecordIds!;

// Loading/error flags before async fetch — pattern not avoidable without a reducer.
// eslint-disable-next-line react-hooks/set-state-in-effect
setLoading(true);

setError(null);

async function fetchContacts() {
Expand Down
1 change: 1 addition & 0 deletions src/components/dev-panel/panels/deploy-tool-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function DeployToolPanel({ onDeployed }: DeployToolPanelProps = {}) {

// Reset the deploy UI when route changes.
useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setLaunchPage(defaultLaunchPage);
}, [defaultLaunchPage]);

Expand Down
3 changes: 3 additions & 0 deletions src/components/dev-panel/panels/user-tools-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ export function UserToolsPanel({ refreshKey = 0, onAuthorizationChange }: UserTo

useEffect(() => {
let cancelled = false;
// Loading/error flags before async fetch.
// eslint-disable-next-line react-hooks/set-state-in-effect
setLoading(true);

setError(null);
(async () => {
try {
Expand Down
5 changes: 5 additions & 0 deletions src/components/template-editor/editor-code-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ export function EditorCodeDialog({ open, onOpenChange }: EditorCodeDialogProps)

useEffect(() => {
if (open) {
// Sync editor's current MJML into dialog state when opened, reset preview.
const mjml = editor.getHtml();
// eslint-disable-next-line react-hooks/set-state-in-effect
setMjmlSource(mjml);

setHtmlPreview('');

setCompileErrors([]);

setCopiedSource(null);
}
}, [open, editor]);
Expand Down
6 changes: 6 additions & 0 deletions src/components/template-editor/editor-export-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,20 @@ export function EditorExportDialog({ open, onOpenChange }: EditorExportDialogPro

useEffect(() => {
if (open) {
// Sync editor's current MJML/project data into dialog state when opened.
const mjml = editor.getHtml();
// eslint-disable-next-line react-hooks/set-state-in-effect
setMjmlSource(mjml);

const projectData = editor.getProjectData();

setJsonState(JSON.stringify(projectData, null, 2));


setHtmlOutput('');

setCompileErrors([]);

setCopiedSource(null);
}
}, [open, editor]);
Expand Down
2 changes: 2 additions & 0 deletions src/components/template-editor/editor-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export function EditorToolbar({ onClose }: EditorToolbarProps) {
}, [editor]);

useEffect(() => {
// Sync undo/redo from GrapesJS UndoManager and subscribe to its change events.
// eslint-disable-next-line react-hooks/set-state-in-effect
updateUndoRedo();

const onUpdate = () => updateUndoRedo();
Expand Down
1 change: 1 addition & 0 deletions src/contexts/user-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function UserProvider({ children }: UserProviderProps) {

useEffect(() => {
if (!isPending && userGuid) {
// eslint-disable-next-line react-hooks/set-state-in-effect
loadUserProfile();
} else if (!isPending && !session) {
setUserProfile(null);
Expand Down
1 change: 0 additions & 1 deletion src/lib/barcode-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* BMP is used because it has a trivial format (no compression) and Word supports it natively.
*/

import type { BarState } from './imb-encoder';
import type { PostnetBar } from './postnet-encoder';

const IMB_DIMS: Record<string, { topOffset: number; barHeight: number }> = {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/providers/ministry-platform/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('MinistryPlatformProvider', () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset singleton
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(MinistryPlatformProvider as any).instance = undefined;
});

Expand Down
2 changes: 1 addition & 1 deletion src/services/fieldManagementService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { FieldManagementService } from './fieldManagementService';
describe('FieldManagementService', () => {
beforeEach(() => {
vi.clearAllMocks();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(FieldManagementService as any).instance = undefined;
});

Expand Down
2 changes: 1 addition & 1 deletion src/services/groupService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { GroupService } from './groupService';
describe('GroupService', () => {
beforeEach(() => {
vi.clearAllMocks();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(GroupService as any).instance = undefined;
});

Expand Down
2 changes: 1 addition & 1 deletion src/services/toolService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ vi.mock('@/lib/providers/ministry-platform', () => {
describe('ToolService', () => {
beforeEach(() => {
vi.clearAllMocks();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(ToolService as any).instance = undefined;
});

Expand Down
2 changes: 1 addition & 1 deletion src/services/userService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('UserService', () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset singleton instance between tests
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(UserService as any).instance = undefined;
});

Expand Down
Loading