Add: Weight field to product admin pages

Added weight (in kilograms) field to both create and edit product pages.
Admins can now set the weight for each product, which is converted to
grams and stored in the database for Royal Mail shipping calculations.
This commit is contained in:
Andymick
2026-07-19 06:50:23 +01:00
parent e150f2aed9
commit f92ed2709c
4 changed files with 50 additions and 1 deletions
+19
View File
@@ -239,6 +239,25 @@ export default async function EditProductPage({ params }: { params: { id: string
</p>
</div>
<div>
<label htmlFor="weightKg" className="tag-label mb-2 block">
Weight (kg, optional)
</label>
<input
id="weightKg"
name="weightKg"
type="number"
step="0.01"
min={0}
defaultValue={row.weightGrams != null ? (row.weightGrams / 1000).toFixed(2) : ''}
className="w-full border border-line bg-paper px-3 py-2 text-sm"
placeholder="e.g. 0.25"
/>
<p className="mt-1 text-xs text-muted">
Used for Royal Mail shipping cost calculation at checkout. Enter weight in kilograms.
</p>
</div>
<div>
<label className="tag-label mb-2 block">Front photo</label>
{product.imageUrl && (
+11
View File
@@ -96,6 +96,10 @@ export async function createProduct(formData: FormData) {
.map((s) => s.trim())
.filter(Boolean);
// Weight in kilograms converted to grams
const weightKgInput = String(formData.get('weightKg') ?? '').trim();
const weightGrams = weightKgInput ? Math.round(Number(weightKgInput) * 1000) : null;
const imageUrl = imageFile && imageFile.size > 0 ? await fileToDataUrl(imageFile) : null;
const imageUrlBack = imageBackFile && imageBackFile.size > 0 ? await fileToDataUrl(imageBackFile) : null;
const colorPhotos = await extractColorPhotos(formData, colors, 'front');
@@ -132,6 +136,7 @@ export async function createProduct(formData: FormData) {
description: description || `${name} — personalize it your way.`,
basePrice: Math.round(priceDollars * 100),
personalisedPrice: personalisedPriceDollars ? Math.round(Number(personalisedPriceDollars) * 100) : null,
weightGrams,
...saleFields,
colors: JSON.stringify(colors),
colorPhotos: Object.keys(colorPhotos).length > 0 ? JSON.stringify(colorPhotos) : null,
@@ -173,6 +178,10 @@ export async function updateProduct(formData: FormData) {
const referenceWidthCm = referenceWidthCmInput ? Number(referenceWidthCmInput) : null;
const referenceHeightCm = referenceHeightCmInput ? Number(referenceHeightCmInput) : null;
// Weight in kilograms converted to grams
const weightKgInput = String(formData.get('weightKg') ?? '').trim();
const weightGrams = weightKgInput ? Math.round(Number(weightKgInput) * 1000) : null;
if (!id) throw new Error('Missing product id.');
if (!name || !category || !priceDollars) {
throw new Error('Name, category, and price are required.');
@@ -234,6 +243,7 @@ export async function updateProduct(formData: FormData) {
showOnProducts: boolean;
referenceWidthCm: number | null;
referenceHeightCm: number | null;
weightGrams: number | null;
imageUrl?: string;
imageUrlBack?: string;
printArea?: string;
@@ -255,6 +265,7 @@ export async function updateProduct(formData: FormData) {
showOnProducts,
referenceWidthCm,
referenceHeightCm,
weightGrams,
};
// Only touch the front photo/print area if a new photo was actually uploaded —
+18
View File
@@ -214,6 +214,24 @@ export default async function NewProductPage() {
</p>
</div>
<div>
<label htmlFor="weightKg" className="tag-label mb-2 block">
Weight (kg, optional)
</label>
<input
id="weightKg"
name="weightKg"
type="number"
step="0.01"
min={0}
className="w-full border border-line bg-paper px-3 py-2 text-sm"
placeholder="e.g. 0.25"
/>
<p className="mt-1 text-xs text-muted">
Used for Royal Mail shipping cost calculation at checkout. Enter weight in kilograms.
</p>
</div>
<div>
<label className="tag-label mb-2 block">Front photo (optional)</label>
<PhotoPrintAreaField />