156 lines
No EOL
6.9 KiB
TypeScript
156 lines
No EOL
6.9 KiB
TypeScript
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import InputError from '@/components/InputError';
|
|
import { useForm } from '@inertiajs/react';
|
|
import { LoaderCircle } from 'lucide-react';
|
|
import { FormEventHandler, useState, useEffect } from 'react';
|
|
import ComponentTitle from '@/components/ui/ComponentTitle';
|
|
|
|
interface AssetFormData {
|
|
symbol: string;
|
|
full_name: string;
|
|
[key: string]: string;
|
|
}
|
|
|
|
interface AssetSetupFormProps {
|
|
onSuccess?: () => void;
|
|
onCancel?: () => void;
|
|
}
|
|
|
|
export default function AssetSetupForm({ onSuccess, onCancel }: AssetSetupFormProps) {
|
|
const { data, setData, post, processing, errors } = useForm<AssetFormData>({
|
|
symbol: '',
|
|
full_name: '',
|
|
});
|
|
|
|
// Load existing asset data on mount
|
|
useEffect(() => {
|
|
const fetchCurrentAsset = async () => {
|
|
try {
|
|
const response = await fetch('/assets/current');
|
|
if (response.ok) {
|
|
const assetData = await response.json();
|
|
if (assetData.asset) {
|
|
setData({
|
|
symbol: assetData.asset.symbol || '',
|
|
full_name: assetData.asset.full_name || '',
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch current asset:', error);
|
|
}
|
|
};
|
|
|
|
fetchCurrentAsset();
|
|
}, []);
|
|
|
|
const [suggestions] = useState([
|
|
{ symbol: 'VWCE', full_name: 'Vanguard FTSE All-World UCITS ETF' },
|
|
{ symbol: 'VTI', full_name: 'Vanguard Total Stock Market ETF' },
|
|
{ symbol: 'SPY', full_name: 'SPDR S&P 500 ETF Trust' },
|
|
{ symbol: 'QQQ', full_name: 'Invesco QQQ Trust' },
|
|
{ symbol: 'IWDA', full_name: 'iShares Core MSCI World UCITS ETF' },
|
|
]);
|
|
|
|
const submit: FormEventHandler = (e) => {
|
|
e.preventDefault();
|
|
|
|
post(route('assets.set-current'), {
|
|
onSuccess: () => {
|
|
if (onSuccess) onSuccess();
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleSuggestionClick = (suggestion: { symbol: string; full_name: string }) => {
|
|
setData({
|
|
symbol: suggestion.symbol,
|
|
full_name: suggestion.full_name,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<div className="space-y-4">
|
|
<ComponentTitle>SET ASSET</ComponentTitle>
|
|
<p className="text-sm text-red-400/60 font-mono">
|
|
[SYSTEM] Specify the asset you want to track
|
|
</p>
|
|
|
|
{/* Quick suggestions */}
|
|
<div className="space-y-2">
|
|
<Label className="text-red-400 font-mono text-xs uppercase tracking-wider">> Quick Select</Label>
|
|
<div className="flex flex-wrap gap-2">
|
|
{suggestions.map((suggestion) => (
|
|
<button
|
|
key={suggestion.symbol}
|
|
type="button"
|
|
onClick={() => handleSuggestionClick(suggestion)}
|
|
className="px-3 py-1 bg-black border border-red-500/50 text-red-400 hover:border-red-400 hover:text-red-300 font-mono text-xs uppercase tracking-wider transition-all rounded-none"
|
|
>
|
|
{suggestion.symbol}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<form onSubmit={submit} className="space-y-4">
|
|
<div>
|
|
<Label htmlFor="symbol" className="text-red-400 font-mono text-xs uppercase tracking-wider">> Asset Symbol</Label>
|
|
<Input
|
|
id="symbol"
|
|
type="text"
|
|
placeholder="VWCE"
|
|
value={data.symbol}
|
|
onChange={(e) => setData('symbol', e.target.value.toUpperCase())}
|
|
className="bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none focus:shadow-[0_0_10px_rgba(239,68,68,0.5)] placeholder:text-red-400/40 transition-all"
|
|
/>
|
|
<p className="text-xs text-red-400/60 mt-1 font-mono">
|
|
[REQUIRED] ticker symbol (e.g. VWCE, VTI, SPY)
|
|
</p>
|
|
<InputError message={errors.symbol} />
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="full_name" className="text-red-400 font-mono text-xs uppercase tracking-wider">> Full Name (Optional)</Label>
|
|
<Input
|
|
id="full_name"
|
|
type="text"
|
|
placeholder="Vanguard FTSE All-World UCITS ETF"
|
|
value={data.full_name}
|
|
onChange={(e) => setData('full_name', e.target.value)}
|
|
className="bg-black border-red-500 text-red-400 focus:border-red-300 font-mono text-sm rounded-none border-2 focus:ring-0 focus:outline-none focus:shadow-[0_0_10px_rgba(239,68,68,0.5)] placeholder:text-red-400/40 transition-all"
|
|
/>
|
|
<p className="text-xs text-red-400/60 mt-1 font-mono">
|
|
[OPTIONAL] human-readable asset name
|
|
</p>
|
|
<InputError message={errors.full_name} />
|
|
</div>
|
|
|
|
<div className="flex gap-3 pt-2">
|
|
<Button
|
|
type="submit"
|
|
disabled={processing || !data.symbol}
|
|
className="flex-1 bg-red-500 hover:bg-red-500 text-black font-mono text-sm font-bold border-red-500 rounded-none border-2 uppercase tracking-wider transition-all glow-red"
|
|
>
|
|
{processing && <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />}
|
|
[EXECUTE]
|
|
</Button>
|
|
{onCancel && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={onCancel}
|
|
className="flex-1 bg-black border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300 font-mono text-sm font-bold rounded-none border-2 uppercase tracking-wider transition-all glow-red"
|
|
>
|
|
[ABORT]
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |