toy availability change to assign date range for rental, toy page show c
This commit is contained in:
parent
d45edc5f88
commit
e52d2d2977
|
|
@ -3,15 +3,18 @@ import Image from 'next/image';
|
||||||
import { mockToys } from '@/lib/mockData';
|
import { mockToys } from '@/lib/mockData';
|
||||||
import type { Toy } from '@/types';
|
import type { Toy } from '@/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import AvailabilityCalendar from '@/components/toys/AvailabilityCalendar';
|
import { Calendar } from '@/components/ui/calendar'; // Using ShadCN calendar
|
||||||
import { ArrowLeft, CalendarDays, DollarSign, MapPin, ShoppingBag, UserCircle2 } from 'lucide-react';
|
import { ArrowLeft, DollarSign, MapPin, ShoppingBag, UserCircle2 } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { getI18n, getStaticParams as getLocaleStaticParams } from '@/locales/server'; // Renamed to avoid conflict
|
import { getI18n, getStaticParams as getLocaleStaticParams } from '@/locales/server';
|
||||||
|
import type { Locale } from '@/locales/server';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { addDays, parseISO } from 'date-fns'; // For date manipulation
|
||||||
|
|
||||||
interface ToyPageProps {
|
interface ToyPageProps {
|
||||||
params: { id: string, locale: string };
|
params: { id: string, locale: Locale };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getToyById(id: string): Promise<Toy | undefined> {
|
async function getToyById(id: string): Promise<Toy | undefined> {
|
||||||
|
|
@ -40,6 +43,17 @@ export default async function ToyPage({ params }: ToyPageProps) {
|
||||||
|
|
||||||
const placeholderHint = toy.category.toLowerCase() || "toy detail";
|
const placeholderHint = toy.category.toLowerCase() || "toy detail";
|
||||||
|
|
||||||
|
const disabledDates = toy.unavailableRanges.map(range => {
|
||||||
|
// react-day-picker expects Date objects for ranges
|
||||||
|
const from = parseISO(range.startDate);
|
||||||
|
const to = parseISO(range.endDate);
|
||||||
|
// If 'to' is inclusive, add a day for the range. react-day-picker's 'to' is exclusive for the visual range.
|
||||||
|
// However, for disabling, if a range is "2023-08-10" to "2023-08-12", all 3 days should be disabled.
|
||||||
|
// The `disabled` prop for DayPicker usually treats `to` as inclusive.
|
||||||
|
return { from, to };
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto py-8 px-4">
|
<div className="container mx-auto py-8 px-4">
|
||||||
<Link href={`/${params.locale}/`} className="inline-flex items-center text-primary hover:underline mb-6 group">
|
<Link href={`/${params.locale}/`} className="inline-flex items-center text-primary hover:underline mb-6 group">
|
||||||
|
|
@ -120,7 +134,22 @@ export default async function ToyPage({ params }: ToyPageProps) {
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<AvailabilityCalendar availability={toy.availability} />
|
<Card className="shadow-md">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl font-headline text-primary">{t('toy_details.availability_calendar_title')}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex justify-center">
|
||||||
|
<Calendar
|
||||||
|
mode="single" // "single" is fine for display, range selection would be for booking
|
||||||
|
disabled={disabledDates} // Disable booked ranges
|
||||||
|
month={new Date()} // Show current month by default
|
||||||
|
className="rounded-md border"
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
<p className="text-xs text-muted-foreground mt-0 pb-4 text-center">
|
||||||
|
{t('toy_details.calendar_note')}
|
||||||
|
</p>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Button size="lg" className="w-full mt-6 transition-transform transform hover:scale-105">
|
<Button size="lg" className="w-full mt-6 transition-transform transform hover:scale-105">
|
||||||
<ShoppingBag className="mr-2 h-5 w-5" /> {t('toy_details.request_to_rent')}
|
<ShoppingBag className="mr-2 h-5 w-5" /> {t('toy_details.request_to_rent')}
|
||||||
|
|
@ -132,7 +161,7 @@ export default async function ToyPage({ params }: ToyPageProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateStaticParams() {
|
export function generateStaticParams() {
|
||||||
const localeParams = getLocaleStaticParams(); // e.g. [{ locale: 'en' }, { locale: 'zh-TW' }]
|
const localeParams = getLocaleStaticParams();
|
||||||
const toyParams = mockToys.map((toy) => ({ id: toy.id }));
|
const toyParams = mockToys.map((toy) => ({ id: toy.id }));
|
||||||
|
|
||||||
return localeParams.flatMap(lang =>
|
return localeParams.flatMap(lang =>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { ToyBrick, Save, PlusCircle, Trash2 } from 'lucide-react';
|
import { ToyBrick, Save, PlusCircle, Trash2 } from 'lucide-react';
|
||||||
import type { Toy } from '@/types';
|
import type { Toy } from '@/types';
|
||||||
|
|
@ -30,9 +29,6 @@ const toyCategoryDefinitions = [
|
||||||
{ key: 'building_blocks', value: 'Building Blocks' },
|
{ key: 'building_blocks', value: 'Building Blocks' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const daysOfWeek = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] as const;
|
|
||||||
|
|
||||||
|
|
||||||
interface AddToyFormProps {
|
interface AddToyFormProps {
|
||||||
initialData?: Partial<Toy>;
|
initialData?: Partial<Toy>;
|
||||||
isEditMode?: boolean;
|
isEditMode?: boolean;
|
||||||
|
|
@ -50,11 +46,8 @@ export default function AddToyForm({ initialData, isEditMode = false }: AddToyFo
|
||||||
const [pricePerDay, setPricePerDay] = useState(initialData?.pricePerDay?.toString() || '0');
|
const [pricePerDay, setPricePerDay] = useState(initialData?.pricePerDay?.toString() || '0');
|
||||||
const [location, setLocation] = useState(initialData?.location || '');
|
const [location, setLocation] = useState(initialData?.location || '');
|
||||||
const [images, setImages] = useState<string[]>(initialData?.images || ['']);
|
const [images, setImages] = useState<string[]>(initialData?.images || ['']);
|
||||||
const [availability, setAvailability] = useState<Toy['availability']>(
|
// unavailableRanges will be initialized as empty or from initialData, but not editable in this form version.
|
||||||
initialData?.availability || {
|
const [unavailableRanges, setUnavailableRanges] = useState<Toy['unavailableRanges']>(initialData?.unavailableRanges || []);
|
||||||
monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: false, sunday: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleImageChange = (index: number, value: string) => {
|
const handleImageChange = (index: number, value: string) => {
|
||||||
|
|
@ -72,34 +65,34 @@ export default function AddToyForm({ initialData, isEditMode = false }: AddToyFo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAvailabilityChange = (day: keyof Toy['availability']) => {
|
|
||||||
setAvailability(prev => ({ ...prev, [day]: !prev[day] }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
if (!name || !description || !category) {
|
if (!name || !description || !category) {
|
||||||
toast({ title: "Missing Fields", description: "Please fill in all required fields.", variant: "destructive" }); // Translate
|
toast({ title: "Missing Fields", description: "Please fill in all required fields.", variant: "destructive" });
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toyData = {
|
const toyData: Partial<Toy> = {
|
||||||
name, description, category,
|
name, description, category,
|
||||||
pricePerDay: parseFloat(pricePerDay) || 0,
|
pricePerDay: parseFloat(pricePerDay) || 0,
|
||||||
location,
|
location,
|
||||||
images: images.filter(img => img.trim() !== ''),
|
images: images.filter(img => img.trim() !== ''),
|
||||||
availability,
|
unavailableRanges: initialData?.unavailableRanges || [], // Preserve existing, or empty for new
|
||||||
};
|
};
|
||||||
|
if (isEditMode && initialData?.id) {
|
||||||
|
toyData.id = initialData.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
console.log("Submitting toy data:", toyData);
|
console.log("Submitting toy data:", toyData);
|
||||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: isEditMode ? "Toy Updated!" : "Toy Added!", // Translate
|
title: isEditMode ? t('add_toy_form.edit_title_toast') : t('add_toy_form.add_title_toast'),
|
||||||
description: `${name} has been successfully ${isEditMode ? 'updated' : 'listed'}.`, // Translate
|
description: t('add_toy_form.success_description_toast', { toyName: name, action: isEditMode ? t('add_toy_form.updated_action_toast') : t('add_toy_form.listed_action_toast')})
|
||||||
});
|
});
|
||||||
router.push(`/${locale}/dashboard/my-toys`);
|
router.push(`/${locale}/dashboard/my-toys`);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
@ -137,7 +130,7 @@ export default function AddToyForm({ initialData, isEditMode = false }: AddToyFo
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{toyCategoryDefinitions.map(catDef => (
|
{toyCategoryDefinitions.map(catDef => (
|
||||||
<SelectItem key={catDef.key} value={catDef.value}>
|
<SelectItem key={catDef.key} value={catDef.value}>
|
||||||
{t(`toy_categories.${catDef.key}` as any)} {/* Use 'as any' if TS complains about template literal type */}
|
{t(`toy_categories.${catDef.key}` as any)}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
@ -180,23 +173,6 @@ export default function AddToyForm({ initialData, isEditMode = false }: AddToyFo
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Label className="text-base">{t('add_toy_form.availability_label')}</Label>
|
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
|
|
||||||
{daysOfWeek.map(day => (
|
|
||||||
<div key={day} className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id={`avail-${day}`}
|
|
||||||
checked={availability[day]}
|
|
||||||
onCheckedChange={() => handleAvailabilityChange(day as keyof Toy['availability'])}
|
|
||||||
disabled={isLoading}
|
|
||||||
/>
|
|
||||||
<Label htmlFor={`avail-${day}`} className="capitalize font-normal text-sm">{day}</Label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CardFooter className="p-0 pt-6">
|
<CardFooter className="p-0 pt-6">
|
||||||
<Button type="submit" className="w-full" size="lg" disabled={isLoading}>
|
<Button type="submit" className="w-full" size="lg" disabled={isLoading}>
|
||||||
{isLoading ? (isEditMode ? t('add_toy_form.saving_button') : t('add_toy_form.listing_button')) : (
|
{isLoading ? (isEditMode ? t('add_toy_form.saving_button') : t('add_toy_form.listing_button')) : (
|
||||||
|
|
@ -212,5 +188,3 @@ export default function AddToyForm({ initialData, isEditMode = false }: AddToyFo
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import type { Toy } from '@/types';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { CheckCircle2, XCircle } from 'lucide-react';
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
|
|
||||||
interface AvailabilityCalendarProps {
|
|
||||||
availability: Toy['availability'];
|
|
||||||
}
|
|
||||||
|
|
||||||
const daysOfWeek = [
|
|
||||||
{ key: 'monday', label: 'Mon' },
|
|
||||||
{ key: 'tuesday', label: 'Tue' },
|
|
||||||
{ key: 'wednesday', label: 'Wed' },
|
|
||||||
{ key: 'thursday', label: 'Thu' },
|
|
||||||
{ key: 'friday', label: 'Fri' },
|
|
||||||
{ key: 'saturday', label: 'Sat' },
|
|
||||||
{ key: 'sunday', label: 'Sun' },
|
|
||||||
] as const; // Use 'as const' for stricter typing of keys
|
|
||||||
|
|
||||||
export default function AvailabilityCalendar({ availability }: AvailabilityCalendarProps) {
|
|
||||||
return (
|
|
||||||
<Card className="shadow-md">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-xl font-headline text-primary">Weekly Availability</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-7 gap-2 text-center">
|
|
||||||
{daysOfWeek.map((day) => {
|
|
||||||
const isAvailable = availability[day.key];
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={day.key}
|
|
||||||
className={cn(
|
|
||||||
"p-3 rounded-md border flex flex-col items-center justify-center space-y-1",
|
|
||||||
isAvailable ? "bg-green-100 border-green-300" : "bg-red-100 border-red-300"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="font-medium text-sm text-foreground/80">{day.label}</span>
|
|
||||||
{isAvailable ? (
|
|
||||||
<CheckCircle2 className="h-6 w-6 text-green-600" />
|
|
||||||
) : (
|
|
||||||
<XCircle className="h-6 w-6 text-red-600" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-muted-foreground mt-4 text-center">
|
|
||||||
This calendar shows general weekly availability. Specific dates may vary.
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
import type { Toy, RentalHistoryEntry } from '@/types';
|
import type { Toy, RentalHistoryEntry } from '@/types';
|
||||||
|
import { addDays, formatISO } from 'date-fns';
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
export const mockToys: Toy[] = [
|
export const mockToys: Toy[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -7,7 +10,10 @@ export const mockToys: Toy[] = [
|
||||||
description: 'A fantastic set of 100 colorful building blocks to spark creativity in young minds. Suitable for ages 3+.',
|
description: 'A fantastic set of 100 colorful building blocks to spark creativity in young minds. Suitable for ages 3+.',
|
||||||
category: 'Educational',
|
category: 'Educational',
|
||||||
images: ['https://placehold.co/600x400.png?text=Building+Blocks', 'https://placehold.co/600x400.png?text=Blocks+Close+Up'],
|
images: ['https://placehold.co/600x400.png?text=Building+Blocks', 'https://placehold.co/600x400.png?text=Blocks+Close+Up'],
|
||||||
availability: { monday: true, tuesday: true, wednesday: false, thursday: true, friday: true, saturday: true, sunday: false },
|
unavailableRanges: [
|
||||||
|
{ startDate: formatISO(addDays(today, 5), { representation: 'date' }), endDate: formatISO(addDays(today, 7), { representation: 'date' }) },
|
||||||
|
{ startDate: formatISO(addDays(today, 15), { representation: 'date' }), endDate: formatISO(addDays(today, 16), { representation: 'date' }) },
|
||||||
|
],
|
||||||
ownerName: 'Alice Wonderland',
|
ownerName: 'Alice Wonderland',
|
||||||
ownerId: 'user1',
|
ownerId: 'user1',
|
||||||
pricePerDay: 5,
|
pricePerDay: 5,
|
||||||
|
|
@ -20,7 +26,9 @@ export const mockToys: Toy[] = [
|
||||||
description: 'Zoom around with this super-fast remote control racing car. Features rechargeable batteries and durable design. Ages 6+.',
|
description: 'Zoom around with this super-fast remote control racing car. Features rechargeable batteries and durable design. Ages 6+.',
|
||||||
category: 'Vehicles',
|
category: 'Vehicles',
|
||||||
images: ['https://placehold.co/600x400.png?text=RC+Car', 'https://placehold.co/600x400.png?text=RC+Car+Controller'],
|
images: ['https://placehold.co/600x400.png?text=RC+Car', 'https://placehold.co/600x400.png?text=RC+Car+Controller'],
|
||||||
availability: { monday: false, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: true, sunday: true },
|
unavailableRanges: [
|
||||||
|
{ startDate: formatISO(addDays(today, 10), { representation: 'date' }), endDate: formatISO(addDays(today, 12), { representation: 'date' }) },
|
||||||
|
],
|
||||||
ownerName: 'Bob The Builder',
|
ownerName: 'Bob The Builder',
|
||||||
ownerId: 'user2',
|
ownerId: 'user2',
|
||||||
pricePerDay: 8,
|
pricePerDay: 8,
|
||||||
|
|
@ -33,7 +41,7 @@ export const mockToys: Toy[] = [
|
||||||
description: 'An educational tablet for kids with games, stories, and learning activities. Parent-approved content. Ages 4-7.',
|
description: 'An educational tablet for kids with games, stories, and learning activities. Parent-approved content. Ages 4-7.',
|
||||||
category: 'Electronics',
|
category: 'Electronics',
|
||||||
images: ['https://placehold.co/600x400.png?text=Kids+Tablet', 'https://placehold.co/600x400.png?text=Tablet+Screen'],
|
images: ['https://placehold.co/600x400.png?text=Kids+Tablet', 'https://placehold.co/600x400.png?text=Tablet+Screen'],
|
||||||
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: false, sunday: false },
|
unavailableRanges: [],
|
||||||
ownerName: 'Carol Danvers',
|
ownerName: 'Carol Danvers',
|
||||||
ownerId: 'user3',
|
ownerId: 'user3',
|
||||||
pricePerDay: 7,
|
pricePerDay: 7,
|
||||||
|
|
@ -46,8 +54,10 @@ export const mockToys: Toy[] = [
|
||||||
description: 'A cuddly and soft large teddy bear, perfect for hugs and comfort. Hypoallergenic materials. All ages.',
|
description: 'A cuddly and soft large teddy bear, perfect for hugs and comfort. Hypoallergenic materials. All ages.',
|
||||||
category: 'Plush Toys',
|
category: 'Plush Toys',
|
||||||
images: ['https://placehold.co/600x400.png?text=Teddy+Bear'],
|
images: ['https://placehold.co/600x400.png?text=Teddy+Bear'],
|
||||||
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: true, sunday: true },
|
unavailableRanges: [
|
||||||
ownerName: 'Alice Wonderland', // Changed owner for variety, was David Copperfield
|
{ startDate: formatISO(addDays(today, 20), { representation: 'date' }), endDate: formatISO(addDays(today, 25), { representation: 'date' }) },
|
||||||
|
],
|
||||||
|
ownerName: 'Alice Wonderland',
|
||||||
ownerId: 'user1',
|
ownerId: 'user1',
|
||||||
pricePerDay: 3,
|
pricePerDay: 3,
|
||||||
location: 'Springfield Gardens',
|
location: 'Springfield Gardens',
|
||||||
|
|
@ -59,8 +69,8 @@ export const mockToys: Toy[] = [
|
||||||
description: 'A 3/4 size acoustic guitar, ideal for children starting their musical journey. Comes with a soft case and picks.',
|
description: 'A 3/4 size acoustic guitar, ideal for children starting their musical journey. Comes with a soft case and picks.',
|
||||||
category: 'Musical',
|
category: 'Musical',
|
||||||
images: ['https://placehold.co/600x400.png?text=Kids+Guitar'],
|
images: ['https://placehold.co/600x400.png?text=Kids+Guitar'],
|
||||||
availability: { monday: true, tuesday: false, wednesday: true, thursday: false, friday: true, saturday: true, sunday: true },
|
unavailableRanges: [],
|
||||||
ownerName: 'Bob The Builder', // Changed owner, was Eve Adamson
|
ownerName: 'Bob The Builder',
|
||||||
ownerId: 'user2',
|
ownerId: 'user2',
|
||||||
pricePerDay: 10,
|
pricePerDay: 10,
|
||||||
location: 'Willow Creek',
|
location: 'Willow Creek',
|
||||||
|
|
@ -72,8 +82,8 @@ export const mockToys: Toy[] = [
|
||||||
description: 'Includes a frisbee, a jump rope, and a set of cones. Perfect for outdoor fun and activities.',
|
description: 'Includes a frisbee, a jump rope, and a set of cones. Perfect for outdoor fun and activities.',
|
||||||
category: 'Outdoor',
|
category: 'Outdoor',
|
||||||
images: ['https://placehold.co/600x400.png?text=Sports+Kit'],
|
images: ['https://placehold.co/600x400.png?text=Sports+Kit'],
|
||||||
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: true, sunday: true },
|
unavailableRanges: [],
|
||||||
ownerName: 'Carol Danvers', // Changed owner, was Frank Castle
|
ownerName: 'Carol Danvers',
|
||||||
ownerId: 'user3',
|
ownerId: 'user3',
|
||||||
pricePerDay: 6,
|
pricePerDay: 6,
|
||||||
location: 'Metro City',
|
location: 'Metro City',
|
||||||
|
|
@ -85,7 +95,7 @@ export const mockRentalHistory: RentalHistoryEntry[] = [
|
||||||
{
|
{
|
||||||
id: 'hist1',
|
id: 'hist1',
|
||||||
userId: 'user1',
|
userId: 'user1',
|
||||||
toy: mockToys[2], // Interactive Learning Tablet from Carol Danvers (user3)
|
toy: mockToys[2],
|
||||||
rentalStartDate: '2024-05-01',
|
rentalStartDate: '2024-05-01',
|
||||||
rentalEndDate: '2024-05-07',
|
rentalEndDate: '2024-05-07',
|
||||||
totalCost: mockToys[2].pricePerDay! * 7,
|
totalCost: mockToys[2].pricePerDay! * 7,
|
||||||
|
|
@ -95,7 +105,7 @@ export const mockRentalHistory: RentalHistoryEntry[] = [
|
||||||
{
|
{
|
||||||
id: 'hist2',
|
id: 'hist2',
|
||||||
userId: 'user1',
|
userId: 'user1',
|
||||||
toy: mockToys[5], // Outdoor Sports Kit from Carol Danvers (user3)
|
toy: mockToys[5],
|
||||||
rentalStartDate: '2024-06-10',
|
rentalStartDate: '2024-06-10',
|
||||||
rentalEndDate: '2024-06-15',
|
rentalEndDate: '2024-06-15',
|
||||||
totalCost: mockToys[5].pricePerDay! * 5,
|
totalCost: mockToys[5].pricePerDay! * 5,
|
||||||
|
|
@ -104,8 +114,8 @@ export const mockRentalHistory: RentalHistoryEntry[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hist3',
|
id: 'hist3',
|
||||||
userId: 'user2', // Different user
|
userId: 'user2',
|
||||||
toy: mockToys[0], // Building Blocks from Alice Wonderland (user1)
|
toy: mockToys[0],
|
||||||
rentalStartDate: '2024-07-01',
|
rentalStartDate: '2024-07-01',
|
||||||
rentalEndDate: '2024-07-10',
|
rentalEndDate: '2024-07-10',
|
||||||
totalCost: mockToys[0].pricePerDay! * 10,
|
totalCost: mockToys[0].pricePerDay! * 10,
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ export default {
|
||||||
'toy_details.price_free': 'Free',
|
'toy_details.price_free': 'Free',
|
||||||
'toy_details.price_per_day': '/day',
|
'toy_details.price_per_day': '/day',
|
||||||
'toy_details.request_to_rent': 'Request to Rent',
|
'toy_details.request_to_rent': 'Request to Rent',
|
||||||
|
'toy_details.availability_calendar_title': 'Availability Calendar',
|
||||||
|
'toy_details.calendar_note': 'Dates shown in gray or crossed out are unavailable.',
|
||||||
'dashboard.layout.loading': 'Loading Dashboard...',
|
'dashboard.layout.loading': 'Loading Dashboard...',
|
||||||
'dashboard.sidebar.user_dashboard': 'User Dashboard',
|
'dashboard.sidebar.user_dashboard': 'User Dashboard',
|
||||||
'dashboard.sidebar.toy_management': 'Toy Management',
|
'dashboard.sidebar.toy_management': 'Toy Management',
|
||||||
|
|
@ -131,6 +133,11 @@ export default {
|
||||||
'add_toy_form.list_button': 'List My Toy',
|
'add_toy_form.list_button': 'List My Toy',
|
||||||
'add_toy_form.saving_button': 'Saving Changes...',
|
'add_toy_form.saving_button': 'Saving Changes...',
|
||||||
'add_toy_form.listing_button': 'Listing Toy...',
|
'add_toy_form.listing_button': 'Listing Toy...',
|
||||||
|
'add_toy_form.edit_title_toast': 'Toy Updated!',
|
||||||
|
'add_toy_form.add_title_toast': 'Toy Added!',
|
||||||
|
'add_toy_form.success_description_toast': '{toyName} has been successfully {action}.',
|
||||||
|
'add_toy_form.updated_action_toast': 'updated',
|
||||||
|
'add_toy_form.listed_action_toast': 'listed',
|
||||||
'dashboard.rentals.title': 'My Rentals',
|
'dashboard.rentals.title': 'My Rentals',
|
||||||
'dashboard.rentals.description': 'Toys you are currently renting from others.',
|
'dashboard.rentals.description': 'Toys you are currently renting from others.',
|
||||||
'dashboard.rentals.no_rentals_title': 'No Active Rentals',
|
'dashboard.rentals.no_rentals_title': 'No Active Rentals',
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ export default {
|
||||||
'toy_details.price_free': '免費',
|
'toy_details.price_free': '免費',
|
||||||
'toy_details.price_per_day': '/天',
|
'toy_details.price_per_day': '/天',
|
||||||
'toy_details.request_to_rent': '請求租借',
|
'toy_details.request_to_rent': '請求租借',
|
||||||
|
'toy_details.availability_calendar_title': '可租借日曆',
|
||||||
|
'toy_details.calendar_note': '灰色或劃掉的日期表示不可租借。',
|
||||||
'dashboard.layout.loading': '正在載入儀表板...',
|
'dashboard.layout.loading': '正在載入儀表板...',
|
||||||
'dashboard.sidebar.user_dashboard': '使用者儀表板',
|
'dashboard.sidebar.user_dashboard': '使用者儀表板',
|
||||||
'dashboard.sidebar.toy_management': '玩具管理',
|
'dashboard.sidebar.toy_management': '玩具管理',
|
||||||
|
|
@ -131,6 +133,11 @@ export default {
|
||||||
'add_toy_form.list_button': '列出我的玩具',
|
'add_toy_form.list_button': '列出我的玩具',
|
||||||
'add_toy_form.saving_button': '儲存變更中...',
|
'add_toy_form.saving_button': '儲存變更中...',
|
||||||
'add_toy_form.listing_button': '列出玩具中...',
|
'add_toy_form.listing_button': '列出玩具中...',
|
||||||
|
'add_toy_form.edit_title_toast': '玩具已更新!',
|
||||||
|
'add_toy_form.add_title_toast': '玩具已新增!',
|
||||||
|
'add_toy_form.success_description_toast': '{toyName} 已成功 {action}。',
|
||||||
|
'add_toy_form.updated_action_toast': '更新',
|
||||||
|
'add_toy_form.listed_action_toast': '列出',
|
||||||
'dashboard.rentals.title': '我的租借',
|
'dashboard.rentals.title': '我的租借',
|
||||||
'dashboard.rentals.description': '您目前正在向他人租借的玩具。',
|
'dashboard.rentals.description': '您目前正在向他人租借的玩具。',
|
||||||
'dashboard.rentals.no_rentals_title': '沒有進行中的租借',
|
'dashboard.rentals.no_rentals_title': '沒有進行中的租借',
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,7 @@ export interface Toy {
|
||||||
description: string;
|
description: string;
|
||||||
category: string;
|
category: string;
|
||||||
images: string[]; // Array of image URLs
|
images: string[]; // Array of image URLs
|
||||||
availability: {
|
unavailableRanges: { startDate: string; endDate: string }[]; // New field for booked/unavailable date ranges
|
||||||
monday: boolean;
|
|
||||||
tuesday: boolean;
|
|
||||||
wednesday: boolean;
|
|
||||||
thursday: boolean;
|
|
||||||
friday: boolean;
|
|
||||||
saturday: boolean;
|
|
||||||
sunday: boolean;
|
|
||||||
};
|
|
||||||
ownerName: string; // Simplified for now
|
ownerName: string; // Simplified for now
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
pricePerDay?: number; // Optional daily rental price
|
pricePerDay?: number; // Optional daily rental price
|
||||||
|
|
@ -36,7 +28,7 @@ export interface DailyAvailability {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RentalHistoryEntry {
|
export interface RentalHistoryEntry {
|
||||||
id: string;
|
id:string;
|
||||||
userId: string; // ID of the user who rented
|
userId: string; // ID of the user who rented
|
||||||
toy: Toy;
|
toy: Toy;
|
||||||
rentalStartDate: string; // ISO date string
|
rentalStartDate: string; // ISO date string
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue