hensei-web/components/job/JobImage/index.tsx
Justin Edmund 3d67622353
Fix i18n migration to next-intl (#430)
## Summary
- Fixed translation key format compatibility with next-intl
- Fixed pluralization format from i18next to next-intl format
- Fixed dynamic translation key error handling
- Updated server components to match API response structure
- Fixed useSearchParams import location

## Changes
- Changed pluralization from `{{count}} items` to `{count} items` format
- Added proper error handling for missing translation keys
- Fixed import paths for next-intl hooks
- Fixed PartyPageClient trying to set non-existent appState.parties

## Test plan
- [x] Verified translations render correctly
- [x] Tested pluralization works with different counts
- [x] Confirmed no console errors about missing translations
- [x] Tested party page functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-03 16:25:59 -07:00

114 lines
2.6 KiB
TypeScript

'use client'
import React, { useState } from 'react'
import { getCookie } from 'cookies-next'
import classNames from 'classnames'
import Button from '~components/common/Button'
import JobAccessoryPopover from '~components/job/JobAccessoryPopover'
import ShieldIcon from '~public/icons/Shield.svg'
import ManaturaIcon from '~public/icons/Manatura.svg'
import styles from './index.module.scss'
interface Props {
job?: Job
currentAccessory?: JobAccessory
accessories?: JobAccessory[]
editable: boolean
user?: User
onAccessorySelected: (value: string) => void
}
const JobImage = ({
job,
currentAccessory,
editable,
accessories,
user,
onAccessorySelected,
}: Props) => {
// Localization
const locale = (getCookie('NEXT_LOCALE') as string) || 'en'
// Component state
const [open, setOpen] = useState(false)
// Refs
const buttonRef = React.createRef<HTMLButtonElement>()
// Static variables
const imageUrl = () => {
let source = ''
if (job) {
const slug = job.name.en.replaceAll(' ', '-').toLowerCase()
const gender = user && user.gender == 1 ? 'b' : 'a'
source = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/jobs/${slug}_${gender}.png`
}
return source
}
const hasAccessory = job && job.accessory
const image = <img alt={job?.name[locale]} src={imageUrl()} />
const classes = classNames({
jobAccessory: true,
selected: open,
})
function handleAccessoryButtonClicked() {
setOpen(!open)
}
function handlePopoverOpenChanged(open: boolean) {
setOpen(open)
}
// Elements
const accessoryButton = () => {
let icon
if (job && job.accessory_type === 1) icon = <ShieldIcon />
else if (job && job.accessory_type === 2) icon = <ManaturaIcon />
return (
<Button
leftAccessoryIcon={icon}
className={classes}
onClick={handleAccessoryButtonClicked}
size="icon"
ref={buttonRef}
/>
)
}
const accessoryPopover = () => {
return job && accessories ? (
<JobAccessoryPopover
buttonref={buttonRef}
currentAccessory={currentAccessory}
accessories={accessories}
editable={editable}
open={open}
job={job}
key={`accessory-${open}`}
onAccessorySelected={onAccessorySelected}
onOpenChange={handlePopoverOpenChanged}
>
{accessoryButton()}
</JobAccessoryPopover>
) : (
''
)
}
return (
<div className={styles.image}>
{hasAccessory ? accessoryPopover() : ''}
{job && job.id !== '-1' ? image : ''}
<div className={styles.overlay} />
</div>
)
}
export default JobImage