Add pre-commit hooks for linting and formatting
Set up pre-commit framework with ruff (backend) and ESLint/Prettier/tsc (frontend) hooks to catch issues locally before CI. Auto-format all frontend files with Prettier to comply with the new check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@ type PresetType = 'true' | 'normal' | 'custom' | null
|
||||
|
||||
function buildLegsFromPreset(
|
||||
regions: Region[],
|
||||
preset: 'true' | 'normal',
|
||||
preset: 'true' | 'normal'
|
||||
): LegEntry[] {
|
||||
const legs: LegEntry[] = []
|
||||
for (const region of regions) {
|
||||
@@ -45,8 +45,11 @@ export function NewGenlocke() {
|
||||
const [name, setName] = useState('')
|
||||
const [legs, setLegs] = useState<LegEntry[]>([])
|
||||
const [preset, setPreset] = useState<PresetType>(null)
|
||||
const [nuzlockeRules, setNuzlockeRules] = useState<NuzlockeRules>(DEFAULT_RULES)
|
||||
const [genlockeRules, setGenlockeRules] = useState<GenlockeRules>({ retireHoF: false })
|
||||
const [nuzlockeRules, setNuzlockeRules] =
|
||||
useState<NuzlockeRules>(DEFAULT_RULES)
|
||||
const [genlockeRules, setGenlockeRules] = useState<GenlockeRules>({
|
||||
retireHoF: false,
|
||||
})
|
||||
const [namingScheme, setNamingScheme] = useState<string | null>(null)
|
||||
const { data: namingCategories } = useNamingCategories()
|
||||
|
||||
@@ -61,7 +64,9 @@ export function NewGenlocke() {
|
||||
}
|
||||
|
||||
const handleGameChange = (index: number, game: Game) => {
|
||||
setLegs((prev) => prev.map((leg, i) => (i === index ? { ...leg, game } : leg)))
|
||||
setLegs((prev) =>
|
||||
prev.map((leg, i) => (i === index ? { ...leg, game } : leg))
|
||||
)
|
||||
}
|
||||
|
||||
const handleRemoveLeg = (index: number) => {
|
||||
@@ -70,7 +75,8 @@ export function NewGenlocke() {
|
||||
|
||||
const handleAddLeg = (region: Region) => {
|
||||
const defaultSlug = region.genlockeDefaults.normalGenlocke
|
||||
const game = region.games.find((g) => g.slug === defaultSlug) ?? region.games[0]
|
||||
const game =
|
||||
region.games.find((g) => g.slug === defaultSlug) ?? region.games[0]
|
||||
if (game) {
|
||||
setLegs((prev) => [...prev, { region: region.name, game }])
|
||||
}
|
||||
@@ -105,17 +111,18 @@ export function NewGenlocke() {
|
||||
navigate('/runs')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const enabledRuleCount = RULE_DEFINITIONS.filter((r) => nuzlockeRules[r.key]).length
|
||||
const enabledRuleCount = RULE_DEFINITIONS.filter(
|
||||
(r) => nuzlockeRules[r.key]
|
||||
).length
|
||||
const totalRuleCount = RULE_DEFINITIONS.length
|
||||
|
||||
// Regions not yet used in legs (for "add leg" picker)
|
||||
const availableRegions = regions?.filter(
|
||||
(r) => !legs.some((l) => l.region === r.name),
|
||||
) ?? []
|
||||
const availableRegions =
|
||||
regions?.filter((r) => !legs.some((l) => l.region === r.name)) ?? []
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-8">
|
||||
@@ -198,7 +205,9 @@ export function NewGenlocke() {
|
||||
: 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'
|
||||
}`}
|
||||
>
|
||||
<div className={`font-medium ${isActive ? 'text-blue-600 dark:text-blue-400' : 'text-gray-900 dark:text-gray-100'}`}>
|
||||
<div
|
||||
className={`font-medium ${isActive ? 'text-blue-600 dark:text-blue-400' : 'text-gray-900 dark:text-gray-100'}`}
|
||||
>
|
||||
{labels[type]}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
@@ -241,11 +250,17 @@ export function NewGenlocke() {
|
||||
)}
|
||||
|
||||
{/* Also allow adding extra regions for presets */}
|
||||
{preset && preset !== 'custom' && availableRegions.length > 0 && legs.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<AddLegDropdown regions={availableRegions} onAdd={handleAddLeg} />
|
||||
</div>
|
||||
)}
|
||||
{preset &&
|
||||
preset !== 'custom' &&
|
||||
availableRegions.length > 0 &&
|
||||
legs.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<AddLegDropdown
|
||||
regions={availableRegions}
|
||||
onAdd={handleAddLeg}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-6 flex justify-between">
|
||||
<button
|
||||
@@ -270,7 +285,10 @@ export function NewGenlocke() {
|
||||
{/* Step 3: Rules */}
|
||||
{step === 3 && (
|
||||
<div>
|
||||
<RulesConfiguration rules={nuzlockeRules} onChange={setNuzlockeRules} />
|
||||
<RulesConfiguration
|
||||
rules={nuzlockeRules}
|
||||
onChange={setNuzlockeRules}
|
||||
/>
|
||||
|
||||
{/* Genlocke-specific rules */}
|
||||
<div className="mt-6 bg-white dark:bg-gray-800 rounded-lg shadow">
|
||||
@@ -301,7 +319,8 @@ export function NewGenlocke() {
|
||||
Keep Hall of Fame
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Pokemon that beat the Elite Four can continue to the next leg
|
||||
Pokemon that beat the Elite Four can continue to the
|
||||
next leg
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
@@ -318,7 +337,8 @@ export function NewGenlocke() {
|
||||
Retire Hall of Fame
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Pokemon that beat the Elite Four are retired and cannot be used in the next leg
|
||||
Pokemon that beat the Elite Four are retired and cannot
|
||||
be used in the next leg
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
@@ -334,7 +354,8 @@ export function NewGenlocke() {
|
||||
Naming Scheme
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Get nickname suggestions from a themed word list when catching Pokemon. Applied to all legs.
|
||||
Get nickname suggestions from a themed word list when catching
|
||||
Pokemon. Applied to all legs.
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-4 py-4">
|
||||
@@ -384,7 +405,9 @@ export function NewGenlocke() {
|
||||
<h3 className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
Name
|
||||
</h3>
|
||||
<p className="text-gray-900 dark:text-gray-100 font-medium">{name}</p>
|
||||
<p className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
@@ -403,7 +426,8 @@ export function NewGenlocke() {
|
||||
{leg.game.name}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400 ml-2">
|
||||
{leg.region.charAt(0).toUpperCase() + leg.region.slice(1)}
|
||||
{leg.region.charAt(0).toUpperCase() +
|
||||
leg.region.slice(1)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -417,22 +441,29 @@ export function NewGenlocke() {
|
||||
</h3>
|
||||
<dl className="space-y-1 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-gray-600 dark:text-gray-400">Nuzlocke Rules</dt>
|
||||
<dt className="text-gray-600 dark:text-gray-400">
|
||||
Nuzlocke Rules
|
||||
</dt>
|
||||
<dd className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{enabledRuleCount} of {totalRuleCount} enabled
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-gray-600 dark:text-gray-400">Hall of Fame</dt>
|
||||
<dt className="text-gray-600 dark:text-gray-400">
|
||||
Hall of Fame
|
||||
</dt>
|
||||
<dd className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{genlockeRules.retireHoF ? 'Retire' : 'Keep'}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<dt className="text-gray-600 dark:text-gray-400">Naming Scheme</dt>
|
||||
<dt className="text-gray-600 dark:text-gray-400">
|
||||
Naming Scheme
|
||||
</dt>
|
||||
<dd className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{namingScheme
|
||||
? namingScheme.charAt(0).toUpperCase() + namingScheme.slice(1)
|
||||
? namingScheme.charAt(0).toUpperCase() +
|
||||
namingScheme.slice(1)
|
||||
: 'None'}
|
||||
</dd>
|
||||
</div>
|
||||
@@ -530,8 +561,18 @@ function LegRow({
|
||||
className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
title="Move up"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 15l7-7 7 7" />
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M5 15l7-7 7 7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
@@ -541,8 +582,18 @@ function LegRow({
|
||||
className="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
title="Move down"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
@@ -551,8 +602,18 @@ function LegRow({
|
||||
className="p-1 text-red-400 hover:text-red-600 dark:hover:text-red-300"
|
||||
title="Remove leg"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -576,8 +637,18 @@ function AddLegDropdown({
|
||||
onClick={() => setOpen(true)}
|
||||
className="flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
|
||||
<svg
|
||||
className="w-4 h-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Add Region
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user