Fix template event creation, add location to templates, fix timestamps
- EventForm: check event?.id instead of event to decide PUT vs POST, fixes "unable to parse string as integer" when creating from template - TemplateForm: add LocationPicker for setting location on templates - docker-compose: set TZ=Australia/Perth on backend and db containers so datetime.now() and PostgreSQL NOW() return local time Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a144945077
commit
5c8b3f895d
@ -3,6 +3,8 @@ services:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
- TZ=Australia/Perth
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
@ -17,6 +19,8 @@ services:
|
||||
ports:
|
||||
- "8000:8000"
|
||||
env_file: .env
|
||||
environment:
|
||||
- TZ=Australia/Perth
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
@ -159,7 +159,7 @@ export default function EventForm({ event, initialStart, initialEnd, initialAllD
|
||||
recurrence_rule: rule,
|
||||
};
|
||||
|
||||
if (event) {
|
||||
if (event?.id) {
|
||||
if (editScope) {
|
||||
payload.edit_scope = editScope;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useState, FormEvent } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import api, { getErrorMessage } from '@/lib/api';
|
||||
import type { EventTemplate } from '@/types';
|
||||
import type { EventTemplate, Location } from '@/types';
|
||||
import { useCalendars } from '@/hooks/useCalendars';
|
||||
import {
|
||||
Dialog,
|
||||
@ -18,6 +18,7 @@ import { Select } from '@/components/ui/select';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import LocationPicker from '@/components/ui/location-picker';
|
||||
|
||||
interface TemplateFormProps {
|
||||
template: EventTemplate | null;
|
||||
@ -29,12 +30,24 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) {
|
||||
const { data: calendars = [] } = useCalendars();
|
||||
const selectableCalendars = calendars.filter((c) => !c.is_system);
|
||||
|
||||
const { data: locations = [] } = useQuery({
|
||||
queryKey: ['locations'],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<Location[]>('/locations');
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
const existingLocation = locations.find((l) => l.id === template?.location_id);
|
||||
const [locationSearch, setLocationSearch] = useState(existingLocation?.name || '');
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: template?.name || '',
|
||||
title: template?.title || '',
|
||||
description: template?.description || '',
|
||||
duration_minutes: template?.duration_minutes?.toString() || '60',
|
||||
calendar_id: template?.calendar_id?.toString() || '',
|
||||
location_id: template?.location_id?.toString() || '',
|
||||
all_day: template?.all_day || false,
|
||||
is_starred: template?.is_starred || false,
|
||||
});
|
||||
@ -47,6 +60,7 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) {
|
||||
description: data.description || null,
|
||||
duration_minutes: parseInt(data.duration_minutes) || 60,
|
||||
calendar_id: data.calendar_id ? parseInt(data.calendar_id) : null,
|
||||
location_id: data.location_id ? parseInt(data.location_id) : null,
|
||||
all_day: data.all_day,
|
||||
is_starred: data.is_starred,
|
||||
};
|
||||
@ -142,6 +156,37 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="tmpl-location">Location</Label>
|
||||
<LocationPicker
|
||||
id="tmpl-location"
|
||||
value={locationSearch}
|
||||
onChange={(val) => {
|
||||
setLocationSearch(val);
|
||||
if (!val) setFormData({ ...formData, location_id: '' });
|
||||
}}
|
||||
onSelect={async (result) => {
|
||||
if (result.source === 'local' && result.location_id) {
|
||||
setFormData({ ...formData, location_id: result.location_id.toString() });
|
||||
} else if (result.source === 'nominatim') {
|
||||
try {
|
||||
const { data: newLoc } = await api.post('/locations', {
|
||||
name: result.name,
|
||||
address: result.address,
|
||||
category: 'other',
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['locations'] });
|
||||
setFormData({ ...formData, location_id: newLoc.id.toString() });
|
||||
toast.success(`Location "${result.name}" created`);
|
||||
} catch {
|
||||
toast.error('Failed to create location');
|
||||
}
|
||||
}
|
||||
}}
|
||||
placeholder="Search for a location..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user