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:
@@ -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
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user