142 lines
4.7 KiB
TypeScript
142 lines
4.7 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
import { api } from '../lib/api';
|
|
import { getCurrentLocation } from '../lib/geolocation';
|
|
|
|
interface Props {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export default function QuickAddWidget({ isOpen, onClose }: Props) {
|
|
const [type, setType] = useState('event');
|
|
const [content, setContent] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [locked, setLocked] = useState(false);
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const today = new Date().toISOString().split('T')[0];
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
checkDiaryStatus();
|
|
setTimeout(() => inputRef.current?.focus(), 50);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
|
e.preventDefault();
|
|
if (content.trim() && !locked && !loading) {
|
|
const form = inputRef.current?.form;
|
|
if (form) {
|
|
const submitEvent = new Event('submit', { bubbles: true, cancelable: true });
|
|
form.dispatchEvent(submitEvent);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if (isOpen) {
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
}
|
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
}, [isOpen, content, locked, loading]);
|
|
|
|
const checkDiaryStatus = async () => {
|
|
const res = await api.getDay(today);
|
|
if (res.data) {
|
|
setLocked(!!res.data.journal);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!content.trim() || locked) return;
|
|
|
|
setLoading(true);
|
|
const location = await getCurrentLocation();
|
|
await api.createEvent(today, type, content, undefined, location ?? undefined);
|
|
setContent('');
|
|
setLoading(false);
|
|
onClose();
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
if (locked) {
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-start justify-center pt-20">
|
|
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
|
|
<div className="relative bg-slate-800 rounded-2xl shadow-2xl w-full max-w-md mx-4 p-6 text-center">
|
|
<p className="text-slate-300 mb-2">Today's diary is locked</p>
|
|
<p className="text-slate-500 text-sm mb-4">Delete the diary to add more events.</p>
|
|
<a
|
|
href={`/journal/${today}`}
|
|
className="inline-block px-4 py-2 bg-purple-600 hover:bg-purple-700 rounded-lg text-sm font-medium transition"
|
|
>
|
|
View Diary
|
|
</a>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-start justify-center pt-20">
|
|
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
|
|
<div className="relative bg-slate-800 rounded-2xl shadow-2xl w-full max-w-md mx-4">
|
|
<form onSubmit={handleSubmit} className="p-4">
|
|
<div className="flex gap-2 mb-3">
|
|
<button
|
|
type="button"
|
|
onClick={() => setType('event')}
|
|
className={`px-3 py-1 rounded text-sm ${type === 'event' ? 'bg-purple-600' : 'bg-slate-700'}`}
|
|
>
|
|
Event
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setType('health')}
|
|
className={`px-3 py-1 rounded text-sm ${type === 'health' ? 'bg-purple-600' : 'bg-slate-700'}`}
|
|
>
|
|
Health
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setType('photo')}
|
|
className={`px-3 py-1 rounded text-sm ${type === 'photo' ? 'bg-purple-600' : 'bg-slate-700'}`}
|
|
>
|
|
Photo
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setType('voice')}
|
|
className={`px-3 py-1 rounded text-sm ${type === 'voice' ? 'bg-purple-600' : 'bg-slate-700'}`}
|
|
>
|
|
Voice
|
|
</button>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<input
|
|
ref={inputRef}
|
|
type="text"
|
|
value={content}
|
|
onChange={(e) => setContent(e.target.value)}
|
|
placeholder={type === 'health' ? 'How are you feeling?' : 'Log an event...'}
|
|
className="flex-1 px-4 py-3 bg-slate-900 rounded-lg border border-slate-700 focus:border-purple-500 focus:outline-none"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={loading || !content.trim()}
|
|
className="px-6 py-3 bg-purple-600 hover:bg-purple-700 rounded-lg font-medium transition disabled:opacity-50"
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|