feat(product): 恢復並實作商品起停用狀態功能,包含表單開關與列表顯示
This commit is contained in:
@@ -152,6 +152,7 @@ class ProductController extends Controller
|
|||||||
'price' => (float) $product->price,
|
'price' => (float) $product->price,
|
||||||
'member_price' => (float) $product->member_price,
|
'member_price' => (float) $product->member_price,
|
||||||
'wholesale_price' => (float) $product->wholesale_price,
|
'wholesale_price' => (float) $product->wholesale_price,
|
||||||
|
'is_active' => (bool) $product->is_active,
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -188,14 +189,17 @@ class ProductController extends Controller
|
|||||||
'price' => 'nullable|numeric|min:0',
|
'price' => 'nullable|numeric|min:0',
|
||||||
'member_price' => 'nullable|numeric|min:0',
|
'member_price' => 'nullable|numeric|min:0',
|
||||||
'wholesale_price' => 'nullable|numeric|min:0',
|
'wholesale_price' => 'nullable|numeric|min:0',
|
||||||
|
'is_active' => 'boolean',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$validated['is_active'] = true;
|
|
||||||
|
|
||||||
if (empty($validated['code'])) {
|
if (empty($validated['code'])) {
|
||||||
$validated['code'] = $this->generateRandomCode();
|
$validated['code'] = $this->generateRandomCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($validated['is_active'])) {
|
||||||
|
$validated['is_active'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
$product = Product::create($validated);
|
$product = Product::create($validated);
|
||||||
|
|
||||||
return redirect()->route('products.index')->with('success', '商品已建立');
|
return redirect()->route('products.index')->with('success', '商品已建立');
|
||||||
@@ -224,6 +228,7 @@ class ProductController extends Controller
|
|||||||
'price' => (float) $product->price,
|
'price' => (float) $product->price,
|
||||||
'member_price' => (float) $product->member_price,
|
'member_price' => (float) $product->member_price,
|
||||||
'wholesale_price' => (float) $product->wholesale_price,
|
'wholesale_price' => (float) $product->wholesale_price,
|
||||||
|
'is_active' => (bool) $product->is_active,
|
||||||
],
|
],
|
||||||
'categories' => Category::where('is_active', true)->get()->map(fn($c) => (object)['id' => $c->id, 'name' => $c->name]),
|
'categories' => Category::where('is_active', true)->get()->map(fn($c) => (object)['id' => $c->id, 'name' => $c->name]),
|
||||||
'units' => Unit::all()->map(fn($u) => (object)['id' => (string) $u->id, 'name' => $u->name, 'code' => $u->code]),
|
'units' => Unit::all()->map(fn($u) => (object)['id' => (string) $u->id, 'name' => $u->name, 'code' => $u->code]),
|
||||||
@@ -251,14 +256,17 @@ class ProductController extends Controller
|
|||||||
'price' => 'nullable|numeric|min:0',
|
'price' => 'nullable|numeric|min:0',
|
||||||
'member_price' => 'nullable|numeric|min:0',
|
'member_price' => 'nullable|numeric|min:0',
|
||||||
'wholesale_price' => 'nullable|numeric|min:0',
|
'wholesale_price' => 'nullable|numeric|min:0',
|
||||||
|
'is_active' => 'boolean',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$validated['is_active'] = true;
|
|
||||||
|
|
||||||
if (empty($validated['code'])) {
|
if (empty($validated['code'])) {
|
||||||
$validated['code'] = $this->generateRandomCode();
|
$validated['code'] = $this->generateRandomCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($validated['is_active'])) {
|
||||||
|
$validated['is_active'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
$product->update($validated);
|
$product->update($validated);
|
||||||
|
|
||||||
if ($request->input('from') === 'show') {
|
if ($request->input('from') === 'show') {
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ class Product extends Model
|
|||||||
'price',
|
'price',
|
||||||
'member_price',
|
'member_price',
|
||||||
'wholesale_price',
|
'wholesale_price',
|
||||||
|
'is_active',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'conversion_rate' => 'decimal:4',
|
'conversion_rate' => 'decimal:4',
|
||||||
|
'is_active' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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 { SearchableSelect } from "@/Components/ui/searchable-select";
|
import { SearchableSelect } from "@/Components/ui/searchable-select";
|
||||||
|
import { Switch } from "@/Components/ui/switch";
|
||||||
import { useForm } from "@inertiajs/react";
|
import { useForm } from "@inertiajs/react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import type { Category, Product } from "@/Pages/Product/Index";
|
import type { Category, Product } from "@/Pages/Product/Index";
|
||||||
@@ -41,6 +42,7 @@ export default function ProductForm({
|
|||||||
price: initialData?.price?.toString() || "",
|
price: initialData?.price?.toString() || "",
|
||||||
member_price: initialData?.member_price?.toString() || "",
|
member_price: initialData?.member_price?.toString() || "",
|
||||||
wholesale_price: initialData?.wholesale_price?.toString() || "",
|
wholesale_price: initialData?.wholesale_price?.toString() || "",
|
||||||
|
is_active: initialData?.is_active ?? true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
@@ -79,6 +81,16 @@ export default function ProductForm({
|
|||||||
<div className="bg-white rounded-xl border border-gray-200 shadow-sm p-6 space-y-6">
|
<div className="bg-white rounded-xl border border-gray-200 shadow-sm p-6 space-y-6">
|
||||||
<div className="flex items-center justify-between border-b pb-2">
|
<div className="flex items-center justify-between border-b pb-2">
|
||||||
<h3 className="text-lg font-bold text-grey-0">基本資訊</h3>
|
<h3 className="text-lg font-bold text-grey-0">基本資訊</h3>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Label htmlFor="is_active" className="text-sm font-medium text-gray-600">
|
||||||
|
商品狀態:{data.is_active ? "啟用" : "停用"}
|
||||||
|
</Label>
|
||||||
|
<Switch
|
||||||
|
id="is_active"
|
||||||
|
checked={data.is_active}
|
||||||
|
onCheckedChange={(checked) => setData("is_active", checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ export default function ProductTable({
|
|||||||
<TableHead>換算率</TableHead>
|
<TableHead>換算率</TableHead>
|
||||||
<TableHead className="w-[200px]">規格</TableHead>
|
<TableHead className="w-[200px]">規格</TableHead>
|
||||||
<TableHead>儲位</TableHead>
|
<TableHead>儲位</TableHead>
|
||||||
|
<TableHead className="text-center">狀態</TableHead>
|
||||||
<TableHead className="text-center">操作</TableHead>
|
<TableHead className="text-center">操作</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -160,6 +161,13 @@ export default function ProductTable({
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<span className="text-sm text-gray-600">{product.location || '-'}</span>
|
<span className="text-sm text-gray-600">{product.location || '-'}</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
{product.is_active ? (
|
||||||
|
<Badge className="bg-green-100 text-green-700 hover:bg-green-100 border-none">啟用</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="secondary" className="bg-gray-100 text-gray-500 hover:bg-gray-100 border-none">停用</Badge>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
<div className="flex justify-center gap-2">
|
<div className="flex justify-center gap-2">
|
||||||
<Link href={route("products.show", product.id)}>
|
<Link href={route("products.show", product.id)}>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface Product {
|
|||||||
price?: number;
|
price?: number;
|
||||||
member_price?: number;
|
member_price?: number;
|
||||||
wholesale_price?: number;
|
wholesale_price?: number;
|
||||||
|
is_active?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user