Skip to content

Commit 04bfd60

Browse files
authored
Merge pull request #408 from TaloDev/develop
Release 0.59.0
2 parents b51bc3a + e7f4b4f commit 04bfd60

File tree

9 files changed

+258
-33
lines changed

9 files changed

+258
-33
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Claude Code Review
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize]
6+
# Optional: Only run on specific file changes
7+
# paths:
8+
# - "src/**/*.ts"
9+
# - "src/**/*.tsx"
10+
# - "src/**/*.js"
11+
# - "src/**/*.jsx"
12+
13+
jobs:
14+
claude-review:
15+
# Optional: Filter by PR author
16+
# if: |
17+
# github.event.pull_request.user.login == 'external-contributor' ||
18+
# github.event.pull_request.user.login == 'new-developer' ||
19+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20+
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
pull-requests: read
25+
issues: read
26+
id-token: write
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 1
33+
34+
- name: Run Claude Code Review
35+
id: claude-review
36+
uses: anthropics/claude-code-action@v1
37+
with:
38+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39+
prompt: |
40+
Please review this pull request and provide feedback on:
41+
- Code quality and best practices
42+
- Potential bugs or issues
43+
- Performance considerations
44+
- Security concerns
45+
46+
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
47+
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
48+
49+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
50+
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options
51+
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
52+
use_sticky_comment: true

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"lint-staged": {
8080
"*.{ts,js,tsx,jsx}": "eslint --fix"
8181
},
82-
"version": "0.58.0",
82+
"version": "0.59.0",
8383
"engines": {
8484
"node": "20.x"
8585
},

src/api/resetStat.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { z } from 'zod'
2+
import api from './api'
3+
import makeValidatedRequest from './makeValidatedRequest'
4+
import { ResetMode } from '../constants/resetMode'
5+
6+
export const resetStat = makeValidatedRequest(
7+
(gameId: number, statId: number, mode: ResetMode) => {
8+
return api.delete(`/games/${gameId}/game-stats/${statId}/player-stats?mode=${mode}`)
9+
},
10+
z.object({
11+
deletedCount: z.number()
12+
})
13+
)

src/modals/LeaderboardDetails.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ const LeaderboardDetails = ({
206206
<div className='p-4 space-y-2 bg-red-100 border border-red-400 rounded'>
207207
<p className='font-semibold'>Danger zone</p>
208208

209-
<p className='space-y-2'>
209+
<div className='space-y-2'>
210210
<p>Once taken, these actions are irreversible.</p>
211211
<div className='flex space-x-2'>
212212
<Button
@@ -235,7 +235,7 @@ const LeaderboardDetails = ({
235235
</span>
236236
</Button>
237237
</div>
238-
</p>
238+
</div>
239239
</div>
240240
}
241241

src/modals/ResetLeaderboardEntries.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function ResetLeaderboardEntries({
4141

4242
try {
4343
const res = await resetLeaderboard(activeGame.id, editingLeaderboard!.id, resetMode)
44-
toast.trigger(`${res.deletedCount} ${res.deletedCount === 1 ? 'entry' : 'entries'} were deleted`)
44+
toast.trigger(`${res.deletedCount} ${res.deletedCount === 1 ? 'entry was' : 'entries were'} deleted`)
4545
goBack(true)
4646
} catch (err) {
4747
setError(buildError(err))
@@ -68,9 +68,9 @@ export function ResetLeaderboardEntries({
6868
</div>
6969

7070
<div className='w-full'>
71-
<label htmlFor='refresh-interval' className='block font-semibold mb-1'>Reset mode</label>
71+
<label htmlFor='reset-mode' className='block font-semibold mb-1'>Reset mode</label>
7272
<Select
73-
inputId='refresh-interval'
73+
inputId='reset-mode'
7474
onChange={(option) => setResetMode(option!.value)}
7575
defaultValue={resetModeOptions.find((option) => option.value === 'dev')}
7676
options={resetModeOptions}

src/modals/ResetStat.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useContext, useState } from 'react'
2+
import Modal from '../components/Modal'
3+
import TextInput from '../components/TextInput'
4+
import Button from '../components/Button'
5+
import buildError from '../utils/buildError'
6+
import ErrorMessage, { TaloError } from '../components/ErrorMessage'
7+
import Select from '../components/Select'
8+
import activeGameState, { SelectedActiveGame } from '../state/activeGameState'
9+
import { useRecoilValue } from 'recoil'
10+
import { GameStat } from '../entities/gameStat'
11+
import clsx from 'clsx'
12+
import { resetStat } from '../api/resetStat'
13+
import ToastContext from '../components/toast/ToastContext'
14+
import { ResetMode, resetModeOptions } from '../constants/resetMode'
15+
16+
type ResetStatProps = {
17+
modalState: [boolean, (goBack: boolean) => void]
18+
editingStat: GameStat | null
19+
}
20+
21+
export function ResetStat({
22+
modalState,
23+
editingStat
24+
}: ResetStatProps) {
25+
const [, goBack] = modalState
26+
const [isLoading, setLoading] = useState(false)
27+
const [error, setError] = useState<TaloError | null>(null)
28+
29+
const activeGame = useRecoilValue(activeGameState) as SelectedActiveGame
30+
31+
const [confirmText, setConfirmText] = useState('')
32+
const [resetMode, setResetMode] = useState<ResetMode>('dev')
33+
34+
const [isMenuOpen, setMenuOpen] = useState(false)
35+
36+
const toast = useContext(ToastContext)
37+
38+
const onResetClick = async () => {
39+
setLoading(true)
40+
setError(null)
41+
42+
try {
43+
const res = await resetStat(activeGame.id, editingStat!.id, resetMode)
44+
toast.trigger(`${res.deletedCount} ${res.deletedCount === 1 ? 'entry was' : 'entries were'} deleted`)
45+
goBack(true)
46+
} catch (err) {
47+
setError(buildError(err))
48+
setLoading(false)
49+
}
50+
}
51+
52+
return (
53+
<Modal
54+
id='reset-stat'
55+
title={`Reset ${editingStat?.name}`}
56+
modalState={[true, () => goBack(true)]}
57+
className={clsx('flex flex-col', {
58+
'md:!h-[55vh]': isMenuOpen
59+
})}
60+
>
61+
<form className='flex flex-col grow'>
62+
<div className='p-4 space-y-4'>
63+
<p>
64+
After clicking <b>Reset</b>, all player stat data matching the selected reset mode will be permanently deleted. This action cannot be undone.
65+
</p>
66+
<div className='p-4 text-sm bg-yellow-50 border border-yellow-300 rounded'>
67+
<p>If Steamworks syncing is enabled, your Talo stat data is deleted instantly, but the Steamworks stats may take up to an hour to update.</p>
68+
</div>
69+
70+
<div className='w-full'>
71+
<label htmlFor='reset-mode' className='block font-semibold mb-1'>Reset mode</label>
72+
<Select
73+
inputId='reset-mode'
74+
onChange={(option) => setResetMode(option!.value)}
75+
defaultValue={resetModeOptions.find((option) => option.value === 'dev')}
76+
options={resetModeOptions}
77+
onMenuOpen={() => setMenuOpen(true)}
78+
onMenuClose={() => setMenuOpen(false)}
79+
/>
80+
</div>
81+
82+
<TextInput
83+
id='confirm'
84+
variant='modal'
85+
label='Type "confirm" below'
86+
placeholder='confirm'
87+
onChange={setConfirmText}
88+
value={confirmText}
89+
/>
90+
91+
{error && <ErrorMessage error={error} />}
92+
</div>
93+
94+
<div className='flex flex-col md:flex-row-reverse md:justify-between space-y-4 md:space-y-0 p-4 border-t border-gray-200 mt-auto'>
95+
<div className='w-full md:w-32'>
96+
<Button
97+
type='button'
98+
isLoading={isLoading}
99+
disabled={confirmText !== 'confirm'}
100+
onClick={onResetClick}
101+
variant='red'
102+
>
103+
Reset
104+
</Button>
105+
</div>
106+
<div className='w-full md:w-32'>
107+
<Button type='button' variant='grey' onClick={() => goBack(false)}>Back</Button>
108+
</div>
109+
</div>
110+
</form>
111+
</Modal>
112+
)
113+
}

src/modals/StatDetails.tsx

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ import canPerformAction, { PermissionBasedAction } from '../utils/canPerformActi
1818
import { GameStat } from '../entities/gameStat'
1919
import { KeyedMutator } from 'swr'
2020
import { z } from 'zod'
21+
import { IconRefresh, IconTrash } from '@tabler/icons-react'
2122

2223
type StatDetailsProps = {
2324
modalState: [boolean, (open: boolean) => void]
2425
mutate: KeyedMutator<{ stats: GameStat[] }>
2526
editingStat: GameStat | null
27+
onResetClick?: () => void
2628
}
2729

2830
const validationSchema = z.object({
@@ -92,7 +94,8 @@ type FormValues = z.infer<typeof validationSchema>
9294
const StatDetails = ({
9395
modalState,
9496
mutate,
95-
editingStat
97+
editingStat,
98+
onResetClick
9699
}: StatDetailsProps) => {
97100
const [, setOpen] = modalState
98101
const [isLoading, setLoading] = useState(false)
@@ -324,6 +327,44 @@ const StatDetails = ({
324327
errors={[errors.maxChange?.message]}
325328
/>
326329

330+
{editingStat && canPerformAction(user, PermissionBasedAction.DELETE_STAT) &&
331+
<div className='p-4 space-y-2 bg-red-100 border border-red-400 rounded'>
332+
<p className='font-semibold'>Danger zone</p>
333+
334+
<div className='space-y-2'>
335+
<p>Once taken, these actions are irreversible.</p>
336+
<div className='flex space-x-2'>
337+
{onResetClick &&
338+
<Button
339+
type='button'
340+
onClick={onResetClick}
341+
variant='red'
342+
className='!w-auto'
343+
icon={<IconRefresh />}
344+
>
345+
<span>
346+
Reset
347+
</span>
348+
</Button>
349+
}
350+
351+
<Button
352+
type='button'
353+
isLoading={isDeleting}
354+
onClick={onDeleteClick}
355+
variant='red'
356+
className='!w-auto'
357+
icon={<IconTrash />}
358+
>
359+
<span>
360+
Delete
361+
</span>
362+
</Button>
363+
</div>
364+
</div>
365+
</div>
366+
}
367+
327368
{apiError && <ErrorMessage error={apiError} />}
328369
</div>
329370

@@ -339,28 +380,13 @@ const StatDetails = ({
339380
</div>
340381
}
341382
{editingStat &&
342-
<div className='flex space-x-2'>
343-
{canPerformAction(user, PermissionBasedAction.DELETE_STAT) &&
344-
<div className='w-full md:w-32'>
345-
<Button
346-
type='button'
347-
isLoading={isDeleting}
348-
onClick={onDeleteClick}
349-
variant='red'
350-
>
351-
Delete
352-
</Button>
353-
</div>
354-
}
355-
356-
<div className='w-full md:w-32'>
357-
<Button
358-
disabled={!isValid || isDeleting}
359-
isLoading={isLoading}
360-
>
361-
Update
362-
</Button>
363-
</div>
383+
<div className='w-full md:w-32'>
384+
<Button
385+
disabled={!isValid || isDeleting}
386+
isLoading={isLoading}
387+
>
388+
Update
389+
</Button>
364390
</div>
365391
}
366392
<div className='w-full md:w-32'>

0 commit comments

Comments
 (0)