From 2c90fc6cc89225fcff831efa7939f556ec1c10cc Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Thu, 19 Feb 2026 16:34:39 +0100 Subject: [PATCH] Fix linting --- .prettierrc | 1 + backend/shared/src/supabase/init.ts | 14 +++-- common/src/envs/is-prod.ts | 2 +- common/src/util/json.ts | 4 +- package.json | 14 ++++- web/.eslintrc.js | 1 + .../add-compatibility-question-button.tsx | 41 +++++++------ .../answer-compatibility-question-content.tsx | 7 ++- .../editor/floating-format-menu.tsx | 22 ++++--- web/components/multi-checkbox.tsx | 57 ++++++++++++++----- web/components/optional-profile-form.tsx | 4 +- web/components/page-base.tsx | 2 +- web/components/required-profile-form.tsx | 4 +- web/components/widgets/input.tsx | 17 +----- web/lib/supabase/db.ts | 2 +- web/lib/supabase/users.ts | 18 +++--- web/lib/util/local.ts | 2 +- web/package.json | 1 + web/tsconfig.json | 4 ++ yarn.lock | 5 ++ 20 files changed, 133 insertions(+), 89 deletions(-) diff --git a/.prettierrc b/.prettierrc index dfb3cd76..8f448141 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,6 +4,7 @@ "semi": false, "trailingComma": "es5", "singleQuote": true, + "bracketSpacing": false, "plugins": ["prettier-plugin-sql"], "overrides": [ { diff --git a/backend/shared/src/supabase/init.ts b/backend/shared/src/supabase/init.ts index 394019a5..f4f90f2e 100644 --- a/backend/shared/src/supabase/init.ts +++ b/backend/shared/src/supabase/init.ts @@ -1,10 +1,10 @@ import pgPromise, {IDatabase, ITask} from 'pg-promise' import {log, metrics} from '../utils' -import {IClient, type IConnectionParameters} from 'pg-promise/typescript/pg-subset' +import {IClient, type IConnectionParameters,} from 'pg-promise/typescript/pg-subset' import {HOUR_MS} from 'common/util/time' import {METRICS_INTERVAL_MS} from 'shared/monitoring/metric-writer' import {getMonitoringContext} from 'shared/monitoring/context' -import {ENV_CONFIG} from "common/envs/constants"; +import {ENV_CONFIG} from 'common/envs/constants' export {SupabaseClient} from 'common/supabase/utils' @@ -32,8 +32,10 @@ export const pgp = pgPromise({ pgp.pg.types.setTypeParser(20, (value: any) => parseInt(value, 10)) pgp.pg.types.setTypeParser(1700, parseFloat) // Type Id 1700 = NUMERIC -export type SupabaseTransaction = ITask<{}> -export type SupabaseDirectClient = IDatabase<{}, IClient> | SupabaseTransaction +export type SupabaseTransaction = ITask +export type SupabaseDirectClient = + | IDatabase + | SupabaseTransaction export function getInstanceId() { return ENV_CONFIG.supabaseInstanceId @@ -87,7 +89,7 @@ const newClient = ( } // Use one connection to avoid WARNING: Creating a duplicate database object for the same connection. -let pgpDirect: IDatabase<{}, IClient> | null = null +let pgpDirect: IDatabase | null = null export function createSupabaseDirectClient( instanceId?: string, @@ -128,7 +130,7 @@ export function createSupabaseDirectClient( return (pgpDirect = client) } -let shortTimeoutPgpClient: IDatabase<{}, IClient> | null = null +let shortTimeoutPgpClient: IDatabase | null = null export const createShortTimeoutDirectClient = () => { if (shortTimeoutPgpClient) return shortTimeoutPgpClient shortTimeoutPgpClient = newClient({ diff --git a/common/src/envs/is-prod.ts b/common/src/envs/is-prod.ts index 444133a1..b7dd3798 100644 --- a/common/src/envs/is-prod.ts +++ b/common/src/envs/is-prod.ts @@ -7,7 +7,7 @@ export const isProd = () => { return process.env.NEXT_PUBLIC_FIREBASE_ENV?.toUpperCase() == 'PROD' } else { // For local scripts and cloud functions - // eslint-disable-next-line @typescript-eslint/no-var-requires + // eslint-disable-next-line @typescript-eslint/no-require-imports const admin = require('firebase-admin') return admin.app().options.projectId === 'compass-130ba' } diff --git a/common/src/util/json.ts b/common/src/util/json.ts index bbdf56e6..e7720696 100644 --- a/common/src/util/json.ts +++ b/common/src/util/json.ts @@ -1,7 +1,7 @@ export const safeJsonParse = (json: string | undefined | null) => { try { - return JSON.parse(json ?? '') - } catch (e) { + return JSON.parse(json ?? '') + } catch { return null } } diff --git a/package.json b/package.json index bfefec0b..4d00e372 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,19 @@ "prepare": "npx husky" }, "lint-staged": { - "*.{ts,tsx,js,jsx}": [ + "web/**/*.{ts,tsx,js,jsx}": [ + "next lint --fix --file", + "next lint --max-warnings 0 --file" + ], + "common/**/*.{ts,tsx,js,jsx}": [ + "eslint --fix", + "eslint --max-warnings 0" + ], + "backend/api/**/*.{ts,tsx,js,jsx}": [ + "eslint --fix", + "eslint --max-warnings 0" + ], + "backend/shared/**/*.{ts,tsx,js,jsx}": [ "eslint --fix", "eslint --max-warnings 0" ] diff --git a/web/.eslintrc.js b/web/.eslintrc.js index d8f3a084..602808e2 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { 'prettier', ], rules: { + "react/prop-types": "off", 'react/display-name': 'off', 'react/no-unescaped-entities': 'off', 'react/jsx-no-target-blank': 'off', diff --git a/web/components/answers/add-compatibility-question-button.tsx b/web/components/answers/add-compatibility-question-button.tsx index 3b8ac6e7..a21e7ebc 100644 --- a/web/components/answers/add-compatibility-question-button.tsx +++ b/web/components/answers/add-compatibility-question-button.tsx @@ -29,11 +29,7 @@ export function AddCompatibilityQuestionButton(props: { if (!user) return null return ( <> - void }) { const {open, setOpen, user, onClose} = props - const [dbQuestion, setDbQuestion] = useState | null>( - null - ) + const [dbQuestion, setDbQuestion] = + useState | null>(null) const afterAddQuestion = (newQuestion: rowFor<'compatibility_prompts'>) => { setDbQuestion(newQuestion) console.debug('setDbQuestion', newQuestion) @@ -125,12 +120,15 @@ function CreateCompatibilityModalContent(props: { const generateJson = () => { // Note the change in the generic type - return options.reduce((obj, item, index) => { - if (item.trim() !== '') { - obj[item] = index // Mapping each option to its index - } - return obj - }, {} as Record) + return options.reduce( + (obj, item, index) => { + if (item.trim() !== '') { + obj[item] = index // Mapping each option to its index + } + return obj + }, + {} as Record + ) } const onAddQuestion = useEvent(async () => { @@ -138,7 +136,7 @@ function CreateCompatibilityModalContent(props: { const data = { question: question, options: generateJson(), - }; + } const newQuestion = await api('create-compatibility-question', data) console.debug('create-compatibility-question', newQuestion, data) const q = newQuestion?.question @@ -146,8 +144,13 @@ function CreateCompatibilityModalContent(props: { afterAddQuestion(q as rowFor<'compatibility_prompts'>) } track('create compatibility question') - } catch (e) { - toast.error(t('answers.add.error_create', 'Error creating compatibility question. Try again?')) + } catch (_e) { + toast.error( + t( + 'answers.add.error_create', + 'Error creating compatibility question. Try again?' + ) + ) } }) @@ -176,7 +179,9 @@ function CreateCompatibilityModalContent(props: { value={options[index]} onChange={(e) => onOptionChange(index, e.target.value)} className="w-full" - placeholder={t('answers.add.option_placeholder', 'Option {n}', {n: String(index + 1)})} + placeholder={t('answers.add.option_placeholder', 'Option {n}', { + n: String(index + 1), + })} rows={1} maxLength={MAX_ANSWER_LENGTH} /> diff --git a/web/components/answers/answer-compatibility-question-content.tsx b/web/components/answers/answer-compatibility-question-content.tsx index f728e9ef..5b92ba05 100644 --- a/web/components/answers/answer-compatibility-question-content.tsx +++ b/web/components/answers/answer-compatibility-question-content.tsx @@ -67,7 +67,7 @@ export const submitCompatibilityAnswer = async ( explanation: input.explanation ?? null, }) - // Track only if the upsert succeeds + // Track only if upsert succeeds track('answer compatibility question', { ...newAnswer, }) @@ -179,7 +179,7 @@ export function AnswerCompatibilityQuestionContent(props: { {shortenedPopularity && ( {shortenedPopularity} @@ -204,7 +204,8 @@ export function AnswerCompatibilityQuestionContent(props: { /> - {t('answers.content.answers_you_accept', "Answers you'll accept")} + {t('answers.content.answers_you_accept', "Answers you'll accept")} diff --git a/web/components/editor/floating-format-menu.tsx b/web/components/editor/floating-format-menu.tsx index b53fdb69..b7ead9f8 100644 --- a/web/components/editor/floating-format-menu.tsx +++ b/web/components/editor/floating-format-menu.tsx @@ -1,12 +1,10 @@ -import { LinkIcon, CheckIcon, TrashIcon } from '@heroicons/react/solid' -import { Editor } from '@tiptap/core' -import { BubbleMenu } from '@tiptap/react' +import {CheckIcon, LinkIcon, TrashIcon} from '@heroicons/react/solid' +import {Editor} from '@tiptap/core' +import {BubbleMenu} from '@tiptap/react' import clsx from 'clsx' -import { getUrl } from 'common/util/parse' -import { useState } from 'react' -import BoldIcon from 'web/lib/icons/bold-icon.svg' -import ItalicIcon from 'web/lib/icons/italic-icon.svg' -import TypeIcon from 'web/lib/icons/type-icon.svg' +import {getUrl} from 'common/util/parse' +import {useState} from 'react' +import {Bold, Italic, Type} from 'lucide-react' // see https://tiptap.dev/guide/menus @@ -40,14 +38,14 @@ export function FloatingFormatMenu(props: { {advanced && ( <> editor.chain().focus().toggleHeading({ level: 1 }).run() } isActive={editor.isActive('heading', { level: 1 })} /> editor.chain().focus().toggleHeading({ level: 2 }).run() } @@ -58,12 +56,12 @@ export function FloatingFormatMenu(props: { )} editor.chain().focus().toggleBold().run()} isActive={editor.isActive('bold')} /> editor.chain().focus().toggleItalic().run()} isActive={editor.isActive('italic')} /> diff --git a/web/components/multi-checkbox.tsx b/web/components/multi-checkbox.tsx index 2f7a0047..ef6cb146 100644 --- a/web/components/multi-checkbox.tsx +++ b/web/components/multi-checkbox.tsx @@ -4,8 +4,8 @@ import {Input} from 'web/components/widgets/input' import {Button} from 'web/components/buttons/button' import clsx from 'clsx' import {useEffect, useMemo, useState} from 'react' -import {useT} from "web/lib/locale"; -import {toKey} from "common/parsing"; +import {useT} from 'web/lib/locale' +import {toKey} from 'common/parsing' export const MultiCheckbox = (props: { // Map of label -> value @@ -20,14 +20,27 @@ export const MultiCheckbox = (props: { // - string: the stored value for the new option; label will be the input text // - { key, value }: explicit label (key) and stored value // - null/undefined to indicate failure/cancellation - addOption?: (label: string) => string | { key: string; value: string } | null | undefined + addOption?: ( + label: string + ) => string | { key: string; value: string } | null | undefined addPlaceholder?: string translationPrefix?: string }) => { - const {choices, selected, onChange, className, optionsClassName, addOption, addPlaceholder, translationPrefix} = props + const { + choices, + selected, + onChange, + className, + optionsClassName, + addOption, + addPlaceholder, + translationPrefix, + } = props // Keep a local merged copy to allow optimistic adds while remaining in sync with props - const [localChoices, setLocalChoices] = useState<{ [key: string]: string }>(choices) + const [localChoices, setLocalChoices] = useState<{ [key: string]: string }>( + choices + ) useEffect(() => { setLocalChoices((prev) => { // If incoming choices changed, merge them with any locally added that still don't collide @@ -56,7 +69,9 @@ export const MultiCheckbox = (props: { let q = newLabel.trim() q = translateOption(q, q).toLowerCase() if (!q) return entries - return entries.filter(([key, value]) => translateOption(key, value).toLowerCase().includes(q)) + return entries.filter(([key, value]) => + translateOption(key, value).toLowerCase().includes(q) + ) }, [addOption, entries, newLabel]) const submitAdd = async () => { @@ -68,8 +83,10 @@ export const MultiCheckbox = (props: { return } // prevent duplicate by label or by value already selected - const existingEntry = Object.entries(localChoices).find(([key, value]) => - translateOption(key, value).toLowerCase() === translateOption(label, label).toLowerCase() + const existingEntry = Object.entries(localChoices).find( + ([key, value]) => + translateOption(key, value).toLowerCase() === + translateOption(label, label).toLowerCase() ) if (existingEntry) { @@ -88,12 +105,13 @@ export const MultiCheckbox = (props: { setAdding(false) return } - const {key, value} = typeof result === 'string' ? {key: label, value: result} : result + const {key, value} = + typeof result === 'string' ? {key: label, value: result} : result setLocalChoices((prev) => ({...prev, [key]: value})) // auto-select newly added option if not already selected if (!selected.includes(value)) onChange([...selected, value]) setNewLabel('') - } catch (e) { + } catch (_e) { setError(t('multi-checkbox.add_failed', 'Failed to add option.')) } finally { setAdding(false) @@ -106,7 +124,10 @@ export const MultiCheckbox = (props: { { setNewLabel(e.target.value) setError(null) @@ -119,7 +140,12 @@ export const MultiCheckbox = (props: { }} className="h-10" /> - {error && {error}} @@ -144,9 +170,12 @@ export const MultiCheckbox = (props: { {addOption && newLabel.trim() && filteredEntries.length === 0 && (
- {t('multi-checkbox.no_matching_options', 'No matching options, feel free to add it.')} + {t( + 'multi-checkbox.no_matching_options', + 'No matching options, feel free to add it.' + )}
)} ) -} \ No newline at end of file +} diff --git a/web/components/optional-profile-form.tsx b/web/components/optional-profile-form.tsx index 6de47a45..60f40f81 100644 --- a/web/components/optional-profile-form.tsx +++ b/web/components/optional-profile-form.tsx @@ -201,7 +201,9 @@ export const OptionalProfileUserForm = (props: { return } } - onSubmit && (await onSubmit()) + if (onSubmit) { + await onSubmit() + } track('submit optional profile') if (user) { let profile diff --git a/web/components/page-base.tsx b/web/components/page-base.tsx index bb13acbc..cbd7fd4d 100644 --- a/web/components/page-base.tsx +++ b/web/components/page-base.tsx @@ -60,7 +60,7 @@ export function PageBase(props: { ) // eslint-disable-next-line react-hooks/rules-of-hooks - trackPageView && useTracking(`view ${trackPageView}`, trackPageProps) + if (trackPageView) useTracking(`view ${trackPageView}`, trackPageProps) useOnline() const [_, setIsAddFundsModalOpen] = useState(false) diff --git a/web/components/required-profile-form.tsx b/web/components/required-profile-form.tsx index 2680c014..02723c57 100644 --- a/web/components/required-profile-form.tsx +++ b/web/components/required-profile-form.tsx @@ -72,10 +72,10 @@ export const RequiredProfileUserForm = (props: { } = userInfo useEffect(() => { - props.setEditUsername && props.setEditUsername(username) + if (props.setEditUsername) props.setEditUsername(username) }, [username]) useEffect(() => { - props.setEditDisplayName && props.setEditDisplayName(name) + if (props.setEditDisplayName) props.setEditDisplayName(name) }, [name]) const canContinue = true diff --git a/web/components/widgets/input.tsx b/web/components/widgets/input.tsx index 8c22a759..51bdf1c2 100644 --- a/web/components/widgets/input.tsx +++ b/web/components/widgets/input.tsx @@ -12,22 +12,10 @@ export const Input = forwardRef( const { error, className, ...rest } = props return ( - <> - - - ) } ) diff --git a/web/lib/supabase/db.ts b/web/lib/supabase/db.ts index b58a9bd5..c9295558 100644 --- a/web/lib/supabase/db.ts +++ b/web/lib/supabase/db.ts @@ -28,7 +28,7 @@ export function updateSupabaseAuth(token?: string) { if (currentToken != token) { currentToken = token if (token == null) { - db['rest'].headers['Authorization'] + // db['rest'].headers['Authorization'] db['realtime'].setAuth(null) } else { db['rest'].headers['Authorization'] = `Bearer ${token}` diff --git a/web/lib/supabase/users.ts b/web/lib/supabase/users.ts index 3e6acf10..743ab930 100644 --- a/web/lib/supabase/users.ts +++ b/web/lib/supabase/users.ts @@ -2,8 +2,8 @@ import {db} from './db' import {run} from 'common/supabase/utils' import {api} from 'web/lib/api' import type {DisplayUser} from 'common/api/user-types' -import {MONTH_MS} from "common/util/time"; -import {APIError} from "common/api/utils"; +import {MONTH_MS} from 'common/util/time' +import {APIError} from 'common/api/utils' export type {DisplayUser} @@ -21,7 +21,7 @@ export async function getUserSafe(userId: string) { export async function getPrivateUserSafe() { try { return await api('me/private') - } catch (e) { + } catch (_e) { return null } } @@ -59,9 +59,7 @@ export async function getDisplayUsers(userIds: string[]) { export async function getProfilesCreations() { const {data} = await run( - db.from('profiles') - .select(`id, created_time`) - .order('created_time') + db.from('profiles').select(`id, created_time`).order('created_time') ) return data } @@ -89,14 +87,12 @@ export async function getCount(table: string) { .select('*', {count: 'exact', head: true}) .gt('last_online_time', new Date(Date.now() - MONTH_MS).toISOString()) // last month ) - return count; + return count } const {count} = await run( - db - .from(table) - .select('*', {count: 'exact', head: true}) + db.from(table).select('*', {count: 'exact', head: true}) ) - return count; + return count } // export async function getNumberProfiles() { diff --git a/web/lib/util/local.ts b/web/lib/util/local.ts index 8b0ce1a7..4dc5f573 100644 --- a/web/lib/util/local.ts +++ b/web/lib/util/local.ts @@ -18,7 +18,7 @@ function getStorageProxy(store: Storage): Store | undefined { setItem: (key: string, value: string) => { try { store.setItem(key, value) - } catch (e) { + } catch { store.clear() // try again store.setItem(key, value) diff --git a/web/package.json b/web/package.json index b9d1f5e2..ebce23b9 100644 --- a/web/package.json +++ b/web/package.json @@ -49,6 +49,7 @@ "heic2any": "0.0.4", "link-preview-js": "3.0.4", "lodash": "4.17.23", + "lucide-react": "0.575.0", "nanoid": "5.0.9", "next": "14.1.0", "posthog-js": "1.234.1", diff --git a/web/tsconfig.json b/web/tsconfig.json index d1a46073..7dcd6d1d 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -14,6 +14,10 @@ "noEmit": true, "esModuleInterop": true, "module": "esnext", + "typeRoots": [ + "./types", + "./node_modules/@types" + ], "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, diff --git a/yarn.lock b/yarn.lock index e506157e..bc2111bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11242,6 +11242,11 @@ lsofi@1.0.0: is-number "^2.1.0" through2 "^2.0.1" +lucide-react@0.575.0: + version "0.575.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.575.0.tgz#90feaa4c140e9693e4ee9426d9927a6b833267ac" + integrity sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg== + lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"