Rust (axum + sqlx) API and worker sharing a Postgres-backed job queue (SKIP LOCKED, heartbeat, reaper, typed statuses), S3 storage with derived keys and a fully private bucket, OIDC photographer login with per-request allowlist checks, client share links with argon2 passwords and lockout, cookie-based image authorization with sliding expiry, hand-rolled spec-compliant streaming ZIP downloads with exact Content-Length, React + Vite gallery frontend, single Docker image, Helm chart for external S3 + Postgres, and Gitea CI. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Stars({ value = 0, onChange, small }) {
|
||||
const [hover, setHover] = useState(0)
|
||||
const shown = hover || value || 0
|
||||
return (
|
||||
<span
|
||||
className={`stars${small ? ' stars-small' : ''}${onChange ? '' : ' stars-readonly'}`}
|
||||
onMouseLeave={() => setHover(0)}
|
||||
>
|
||||
{[1, 2, 3, 4, 5].map((n) => (
|
||||
<button
|
||||
key={n}
|
||||
type="button"
|
||||
className={n <= shown ? 'star filled' : 'star'}
|
||||
disabled={!onChange}
|
||||
onMouseEnter={() => onChange && setHover(n)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
// Clicking the current rating clears it.
|
||||
onChange(n === value ? 0 : n)
|
||||
}}
|
||||
title={onChange ? `${n} star${n > 1 ? 's' : ''}` : undefined}
|
||||
>
|
||||
★
|
||||
</button>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user