Feature: Add database-driven shipping rates management
- Add ShippingRate model to Prisma schema with service name, base price, per-kg surcharge, estimated delivery days, and international flag - Create migration to add shipping_rates table - Update seed.ts to populate initial Royal Mail rates (domestic and international) - Refactor getRoyalMailShippingQuotes to fetch rates from database with fallback to defaults - Add /admin/shipping-rates page for managing rates - Add ShippingRatesForm component with inline editing and add/remove functionality - Add server actions for saving and deleting rates - Add "Shipping rates" link to admin navigation Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 4.5
parent
617a5acbb2
commit
f770428556
@@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ShippingRate" (
|
||||
"id" TEXT NOT NULL,
|
||||
"service" TEXT NOT NULL,
|
||||
"basePrice" INTEGER NOT NULL,
|
||||
"pricePerKg" INTEGER NOT NULL DEFAULT 0,
|
||||
"estimatedDays" INTEGER NOT NULL,
|
||||
"isInternational" BOOLEAN NOT NULL DEFAULT false,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ShippingRate_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ShippingRate_service_key" ON "ShippingRate"("service");
|
||||
@@ -546,3 +546,17 @@ model BankReconciliation {
|
||||
confidence Int @default(100) // 0-100, how confident the match is (100=exact amount+date, <100=fuzzy match)
|
||||
notes String? // manual notes about the match
|
||||
}
|
||||
|
||||
// Shipping rates configuration — managed from admin panel
|
||||
// Allows easy updates to Royal Mail pricing without code changes
|
||||
model ShippingRate {
|
||||
id String @id @default(cuid())
|
||||
service String @unique // e.g., "Royal Mail 24®", "International Signed"
|
||||
basePrice Int // cents — base price for this service
|
||||
pricePerKg Int @default(0) // cents — additional cost per kg (mainly for international)
|
||||
estimatedDays Int // estimated delivery days
|
||||
isInternational Boolean @default(false)
|
||||
active Boolean @default(true)
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
|
||||
+49
-1
@@ -123,6 +123,44 @@ const products: Array<{
|
||||
},
|
||||
];
|
||||
|
||||
const shippingRates = [
|
||||
{
|
||||
service: 'Special Delivery Guaranteed by 9am',
|
||||
basePrice: 895,
|
||||
pricePerKg: 0,
|
||||
estimatedDays: 1,
|
||||
isInternational: false,
|
||||
},
|
||||
{
|
||||
service: 'Royal Mail 24®',
|
||||
basePrice: 495,
|
||||
pricePerKg: 0,
|
||||
estimatedDays: 1,
|
||||
isInternational: false,
|
||||
},
|
||||
{
|
||||
service: 'Royal Mail 48®',
|
||||
basePrice: 295,
|
||||
pricePerKg: 0,
|
||||
estimatedDays: 3,
|
||||
isInternational: false,
|
||||
},
|
||||
{
|
||||
service: 'International Signed',
|
||||
basePrice: 1495,
|
||||
pricePerKg: 300,
|
||||
estimatedDays: 14,
|
||||
isInternational: true,
|
||||
},
|
||||
{
|
||||
service: 'International Economy',
|
||||
basePrice: 1095,
|
||||
pricePerKg: 200,
|
||||
estimatedDays: 21,
|
||||
isInternational: true,
|
||||
},
|
||||
];
|
||||
|
||||
async function main() {
|
||||
for (const c of categories) {
|
||||
await prisma.category.upsert({
|
||||
@@ -153,7 +191,17 @@ async function main() {
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Seeded ${categories.length} categories and ${products.length} products.`);
|
||||
for (const rate of shippingRates) {
|
||||
await prisma.shippingRate.upsert({
|
||||
where: { service: rate.service },
|
||||
update: { ...rate },
|
||||
create: { ...rate },
|
||||
});
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Seeded ${categories.length} categories, ${products.length} products, and ${shippingRates.length} shipping rates.`
|
||||
);
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user