feat: update sidebar nav, add open play toggle, mark courts 5-6 as open play

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ivan
2026-03-02 03:55:13 +00:00
parent 0753edb275
commit a882c8698d
3 changed files with 28 additions and 5 deletions

View File

@@ -37,6 +37,7 @@ interface Court {
status: string; status: string;
siteId: string; siteId: string;
site?: { name: string }; site?: { name: string };
isOpenPlay?: boolean;
} }
interface User { interface User {
@@ -479,7 +480,14 @@ export default function SettingsPage() {
<tbody> <tbody>
{courts.map((court) => ( {courts.map((court) => (
<tr key={court.id} className="border-b border-primary-50 hover:bg-primary-50/50"> <tr key={court.id} className="border-b border-primary-50 hover:bg-primary-50/50">
<td className="px-4 py-3 font-medium text-primary-800">{court.name}</td> <td className="px-4 py-3 font-medium text-primary-800">
{court.name}
{court.isOpenPlay && (
<span className="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-700">
Open Play
</span>
)}
</td>
<td className="px-4 py-3 text-primary-600">{court.site?.name || "-"}</td> <td className="px-4 py-3 text-primary-600">{court.site?.name || "-"}</td>
<td className="px-4 py-3 text-primary-600 capitalize">{court.type}</td> <td className="px-4 py-3 text-primary-600 capitalize">{court.type}</td>
<td className="px-4 py-3 text-primary-600">${court.hourlyRate}</td> <td className="px-4 py-3 text-primary-600">${court.hourlyRate}</td>
@@ -709,6 +717,7 @@ function CourtFormModal({
const [hourlyRate, setHourlyRate] = useState(court?.hourlyRate?.toString() || "300"); const [hourlyRate, setHourlyRate] = useState(court?.hourlyRate?.toString() || "300");
const [peakHourlyRate, setPeakHourlyRate] = useState(court?.peakHourlyRate?.toString() || ""); const [peakHourlyRate, setPeakHourlyRate] = useState(court?.peakHourlyRate?.toString() || "");
const [status, setStatus] = useState(court?.status || "active"); const [status, setStatus] = useState(court?.status || "active");
const [isOpenPlay, setIsOpenPlay] = useState(court?.isOpenPlay ?? false);
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -719,6 +728,7 @@ function CourtFormModal({
hourlyRate: parseFloat(hourlyRate), hourlyRate: parseFloat(hourlyRate),
peakHourlyRate: peakHourlyRate ? parseFloat(peakHourlyRate) : null, peakHourlyRate: peakHourlyRate ? parseFloat(peakHourlyRate) : null,
status, status,
isOpenPlay,
}); });
}; };
@@ -797,6 +807,18 @@ function CourtFormModal({
<option value="inactive">Inactive</option> <option value="inactive">Inactive</option>
</select> </select>
</div> </div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="isOpenPlay"
checked={isOpenPlay}
onChange={(e) => setIsOpenPlay(e.target.checked)}
className="rounded border-primary-300"
/>
<label htmlFor="isOpenPlay" className="text-sm text-primary-700">
Open Play Court (free, for group scheduling)
</label>
</div>
<div className="flex gap-3 pt-4"> <div className="flex gap-3 pt-4">
<Button type="button" variant="outline" onClick={onClose} className="flex-1"> <Button type="button" variant="outline" onClick={onClose} className="flex-1">
Cancel Cancel

View File

@@ -4,8 +4,8 @@ import Link from 'next/link';
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';
import { import {
LayoutDashboard, LayoutDashboard,
Calendar, Radio,
Users, UserCircle,
CreditCard, CreditCard,
BarChart3, BarChart3,
Settings, Settings,
@@ -20,8 +20,8 @@ interface NavItem {
const navItems: NavItem[] = [ const navItems: NavItem[] = [
{ label: 'Dashboard', href: '/dashboard', icon: LayoutDashboard }, { label: 'Dashboard', href: '/dashboard', icon: LayoutDashboard },
{ label: 'Bookings', href: '/bookings', icon: Calendar }, { label: 'Live Courts', href: '/live', icon: Radio },
{ label: 'Players', href: '/clients', icon: Users }, { label: 'Clients', href: '/clients', icon: UserCircle },
{ label: 'Memberships', href: '/memberships', icon: CreditCard }, { label: 'Memberships', href: '/memberships', icon: CreditCard },
{ label: 'Reports', href: '/reports', icon: BarChart3 }, { label: 'Reports', href: '/reports', icon: BarChart3 },
{ label: 'Settings', href: '/settings', icon: Settings }, { label: 'Settings', href: '/settings', icon: Settings },

View File

@@ -101,6 +101,7 @@ async function main() {
type: CourtType.OUTDOOR, type: CourtType.OUTDOOR,
status: CourtStatus.AVAILABLE, status: CourtStatus.AVAILABLE,
pricePerHour: 300, pricePerHour: 300,
isOpenPlay: i >= 5,
description: 'Outdoor court with night lighting', description: 'Outdoor court with night lighting',
features: ['Night lighting', 'Court dividers'], features: ['Night lighting', 'Court dividers'],
displayOrder: i, displayOrder: i,