This commit is contained in:
myrmidex 2025-07-10 17:37:30 +02:00
parent 52f8ae2fd1
commit 12c377c92c
5 changed files with 240 additions and 0 deletions

View file

@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Pricing;
use App\Http\Controllers\Controller;
use App\Models\Pricing\AssetPrice;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class PricingController extends Controller
{
public function current(): JsonResponse
{
$price = AssetPrice::current();
return response()->json([
'current_price' => $price,
]);
}
public function update(Request $request): JsonResponse
{
$validated = $request->validate([
'date' => 'required|date|before_or_equal:today',
'price' => 'required|numeric|min:0.0001',
]);
$assetPrice = AssetPrice::updatePrice($validated['date'], $validated['price']);
return response()->json([
'success' => true,
'message' => 'Asset price updated successfully!',
'data' => $assetPrice,
]);
}
public function history(Request $request): JsonResponse
{
$limit = $request->get('limit', 30);
$history = AssetPrice::history($limit);
return response()->json($history);
}
public function forDate(Request $request, string $date): JsonResponse
{
$price = AssetPrice::forDate($date);
return response()->json([
'date' => $date,
'price' => $price,
]);
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Models\Pricing;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
/**
* @method static latest(string $string)
* @method static where(string $string, string $string1, string $date)
* @method static updateOrCreate(string[] $array, float[] $array1)
* @method static orderBy(string $string, string $string1)
* @property Carbon $date
* @property float $price
*/
class AssetPrice extends Model
{
use HasFactory;
protected $fillable = [
'date',
'price',
];
protected $casts = [
'date' => 'date',
'price' => 'decimal:4',
];
public static function current(): ?float
{
$latestPrice = static::latest('date')->first();
return $latestPrice ? $latestPrice->price : null;
}
public static function forDate(string $date): ?float
{
$price = static::where('date', '<=', $date)
->orderBy('date', 'desc')
->first();
return $price ? $price->price : null;
}
public static function updatePrice(string $date, float $price): self
{
return static::updateOrCreate(
['date' => $date],
['price' => $price]
);
}
public static function history(int $limit = 30): Collection
{
return static::orderBy('date', 'desc')->limit($limit)->get();
}
}

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('asset_prices', function (Blueprint $table) {
$table->id();
$table->date('date');
$table->decimal('price', 10, 4);
$table->timestamps();
$table->unique('date');
$table->index('date');
});
}
public function down(): void
{
Schema::dropIfExists('asset_prices');
}
};

View file

@ -0,0 +1,91 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
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 } from 'react';
interface PriceUpdateFormData {
date: string;
price: string;
[key: string]: string;
}
interface UpdatePriceFormProps {
currentPrice?: number;
className?: string;
}
export default function UpdatePriceForm({ currentPrice, className }: UpdatePriceFormProps) {
const { data, setData, post, processing, errors } = useForm<PriceUpdateFormData>({
date: new Date().toISOString().split('T')[0], // Today's date in YYYY-MM-DD format
price: currentPrice?.toString() || '',
});
const submit: FormEventHandler = (e) => {
e.preventDefault();
post(route('pricing.update'), {
onSuccess: () => {
// Keep the date, reset only price if needed
// User might want to update same day multiple times
},
});
};
return (
<Card className={className}>
<CardHeader>
<CardTitle>Update Asset Price</CardTitle>
{currentPrice && (
<p className="text-sm text-neutral-600 dark:text-neutral-400">
Current price: {currentPrice.toFixed(4)}
</p>
)}
</CardHeader>
<CardContent>
<form onSubmit={submit} className="space-y-4">
<div>
<Label htmlFor="date">Price Date</Label>
<Input
id="date"
type="date"
value={data.date}
onChange={(e) => setData('date', e.target.value)}
max={new Date().toISOString().split('T')[0]}
/>
<InputError message={errors.date} />
</div>
<div>
<Label htmlFor="price">Asset Price ()</Label>
<Input
id="price"
type="number"
step="0.0001"
min="0"
placeholder="123.4567"
value={data.price}
onChange={(e) => setData('price', e.target.value)}
/>
<p className="text-xs text-neutral-500 mt-1">
Price per unit/share of the asset
</p>
<InputError message={errors.price} />
</div>
<Button
type="submit"
disabled={processing}
className="w-full"
>
{processing && <LoaderCircle className="mr-2 h-4 w-4 animate-spin" />}
Update Price
</Button>
</form>
</CardContent>
</Card>
);
}

View file

@ -1,6 +1,7 @@
<?php <?php
use App\Http\Controllers\Transactions\PurchaseController; use App\Http\Controllers\Transactions\PurchaseController;
use App\Http\Controllers\Pricing\PricingController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Inertia\Inertia; use Inertia\Inertia;
@ -20,5 +21,13 @@
Route::delete('/{purchase}', [PurchaseController::class, 'destroy'])->name('destroy'); Route::delete('/{purchase}', [PurchaseController::class, 'destroy'])->name('destroy');
}); });
// Pricing routes
Route::prefix('pricing')->name('pricing.')->group(function () {
Route::get('/current', [PricingController::class, 'current'])->name('current');
Route::post('/update', [PricingController::class, 'update'])->name('update');
Route::get('/history', [PricingController::class, 'history'])->name('history');
Route::get('/date/{date}', [PricingController::class, 'forDate'])->name('for-date');
});
require __DIR__.'/settings.php'; require __DIR__.'/settings.php';
require __DIR__.'/auth.php'; require __DIR__.'/auth.php';