pelagia-portal/App/pelagia-portal/prisma/seed.ts
Hardik f95b3279c8 feat(line-items): separate name (mandatory, searchable) from description (optional)
- Add POLineItem.name column; migrate existing description→name; description is now optional
- NameCell component: name input with fuzzy product search, description input stacked below
- Read-only view shows name prominently, description in subdued text below
- All server actions (create, edit, manager edit, import) updated to read/write name
- ParsedImportLine.description renamed to .name throughout import parser and form
- Seed data updated; CLAUDE.md added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 03:37:53 +05:30

655 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient, Role } from "@prisma/client";
import bcrypt from "bcryptjs";
const db = new PrismaClient();
async function main() {
console.log("Seeding database...");
const hash = (p: string) => bcrypt.hash(p, 12);
// Users
const admin = await db.user.upsert({
where: { email: "admin@pelagia.local" },
update: {},
create: {
employeeId: "EMP-001",
email: "admin@pelagia.local",
name: "System Admin",
passwordHash: await hash("admin1234"),
role: Role.ADMIN,
},
});
const manager = await db.user.upsert({
where: { email: "manager@pelagia.local" },
update: {},
create: {
employeeId: "EMP-002",
email: "manager@pelagia.local",
name: "James Hartwell",
passwordHash: await hash("manager1234"),
role: Role.MANAGER,
},
});
const technical = await db.user.upsert({
where: { email: "tech@pelagia.local" },
update: {},
create: {
employeeId: "EMP-003",
email: "tech@pelagia.local",
name: "Maria Santos",
passwordHash: await hash("tech1234"),
role: Role.TECHNICAL,
},
});
const accounts = await db.user.upsert({
where: { email: "accounts@pelagia.local" },
update: {},
create: {
employeeId: "EMP-004",
email: "accounts@pelagia.local",
name: "Chen Wei",
passwordHash: await hash("accounts1234"),
role: Role.ACCOUNTS,
},
});
await db.user.upsert({
where: { email: "manning@pelagia.local" },
update: {},
create: {
employeeId: "EMP-005",
email: "manning@pelagia.local",
name: "Raj Patel",
passwordHash: await hash("manning1234"),
role: Role.MANNING,
},
});
// Vessels
const mv1 = await db.vessel.upsert({
where: { imoNumber: "IMO9876543" },
update: {},
create: { name: "MV Pelagia Star", imoNumber: "IMO9876543" },
});
const mv2 = await db.vessel.upsert({
where: { imoNumber: "IMO9123456" },
update: {},
create: { name: "MV Aegean Wind", imoNumber: "IMO9123456" },
});
await db.vessel.upsert({
where: { imoNumber: "IMO9654321" },
update: {},
create: { name: "MV Poseidon", imoNumber: "IMO9654321" },
});
// Accounts
const acc1 = await db.account.upsert({
where: { code: "TECH-OPS" },
update: {},
create: { code: "TECH-OPS", name: "Technical Operations", description: "Engine and deck equipment" },
});
const acc2 = await db.account.upsert({
where: { code: "CREW-MGT" },
update: {},
create: { code: "CREW-MGT", name: "Crew Management", description: "Manning and crew welfare" },
});
await db.account.upsert({
where: { code: "FUEL-BNK" },
update: {},
create: { code: "FUEL-BNK", name: "Fuel & Bunkers", description: "Fuel procurement" },
});
// Vendors
const vendor1 = await db.vendor.upsert({
where: { vendorId: "VND-0001" },
update: {},
create: {
name: "Marine Parts International",
vendorId: "VND-0001",
contactName: "Tony Nguyen",
contactEmail: "tnguyen@marinepartsinternational.com",
address: "Plot 12, MIDC Industrial Area, Turbhe, Navi Mumbai 400705",
gstin: "27AABCM1234A1Z5",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0002" },
update: {},
create: {
name: "Global Crew Supplies",
vendorId: "VND-0002",
contactName: "Sarah Kim",
contactEmail: "sarah@globalcrewsupplies.com",
address: "14B, Harbour Street, Mazgaon, Mumbai 400010",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0003" },
update: {},
create: {
name: "Atlas Ship Chandlers",
vendorId: "VND-0003",
contactName: "Marco Rossi",
contactEmail: "marco@atlaschandlers.com",
address: "Unit 7, Sassoon Dock, Colaba, Mumbai 400005",
isVerified: false,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0004" },
update: {},
create: {
name: "Apar Industries Ltd",
vendorId: "VND-0004",
contactName: "Nikhil Mumbaikar",
contactMobile: "7208055636",
contactEmail: "nikhil.mumbaikar@apar.com",
address: "18, TTC MIDC Industrial Area, Thane Belapur Road, Opp Rabale Railway Stn, Navi Mumbai 400701",
gstin: "27AAACG1840M1ZL",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0005" },
update: {},
create: {
name: "Neptune Marine Stores",
vendorId: "VND-0005",
contactName: "Ravi Sharma",
contactMobile: "9821234567",
contactEmail: "ravi@neptunemarine.in",
address: "B-204, Jogeshwari Industrial Estate, Mumbai 400060",
gstin: "27AADCN5678B1ZK",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0006" },
update: {},
create: {
name: "Seaview Hydraulics Pvt Ltd",
vendorId: "VND-0006",
contactName: "Anand Pillai",
contactMobile: "9867543210",
contactEmail: "anand@seaviewhydraulics.com",
address: "Plot 45, Phase II, Ambad MIDC, Nashik 422010",
gstin: "27AABCS9876C1ZM",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0007" },
update: {},
create: {
name: "Pacific Safety Equipment",
vendorId: "VND-0007",
contactName: "Priya Nair",
contactEmail: "priya@pacificsafety.com",
address: "22, Linking Road, Bandra West, Mumbai 400050",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0008" },
update: {},
create: {
name: "Mumbai Ship Stores",
vendorId: "VND-0008",
contactName: "Deepak Mehta",
contactMobile: "9876543210",
contactEmail: "deepak@mumbaishipstores.com",
address: "78, Ballard Estate, Fort, Mumbai 400001",
gstin: "27AAACM4567D1ZJ",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0009" },
update: {},
create: {
name: "Bharat Navigation Systems",
vendorId: "VND-0009",
contactName: "Suresh Kumar",
contactEmail: "suresh@bharatnav.in",
address: "67, M.G. Road, Pune 411001",
isVerified: false,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0010" },
update: {},
create: {
name: "Coastal Rope & Rigging",
vendorId: "VND-0010",
contactName: "James D'Souza",
contactMobile: "9823456789",
contactEmail: "james@coastalrope.com",
address: "Sector 12, Kandivali East, Mumbai 400101",
gstin: "27AABCC2345E1ZP",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0011" },
update: {},
create: {
name: "Indotech Filters & Fluids",
vendorId: "VND-0011",
contactName: "Meenakshi Srinivasan",
contactEmail: "msrinivasan@indotech.co.in",
address: "E-12, Peenya Industrial Area Phase 1, Bengaluru 560058",
gstin: "29AABCI5678F1ZL",
isVerified: true,
},
});
await db.vendor.upsert({
where: { vendorId: "VND-0012" },
update: {},
create: {
name: "Eastern Electro Marine",
vendorId: "VND-0012",
contactName: "Rahul Das",
contactMobile: "9432167890",
contactEmail: "rahul.das@easternelectro.com",
address: "12A, Strand Road, Kolkata 700001",
gstin: "19AABCE6789G1ZK",
isVerified: false,
},
});
// Products
const prod1 = await db.product.upsert({
where: { code: "PART-TURBO-SEAL" },
update: {},
create: {
code: "PART-TURBO-SEAL",
name: "Turbocharger Seal Kit",
description: "Replacement seal kit for main engine turbocharger",
lastPrice: 1200,
},
});
const prod2 = await db.product.upsert({
where: { code: "PART-FP-PUMP" },
update: {},
create: {
code: "PART-FP-PUMP",
name: "High-Pressure Fuel Pump",
description: "Main engine high-pressure fuel pump assembly",
lastPrice: 4800,
},
});
await db.product.upsert({
where: { code: "SAFE-LIFEJKT" },
update: {},
create: {
code: "SAFE-LIFEJKT",
name: "Life Jacket (SOLAS)",
description: "SOLAS-approved adult life jacket",
lastPrice: 120,
},
});
await db.product.upsert({
where: { code: "SAFE-EXTG-9KG" },
update: {},
create: {
code: "SAFE-EXTG-9KG",
name: "Fire Extinguisher 9kg",
description: "Dry powder fire extinguisher, 9kg",
lastPrice: 200,
},
});
await db.product.upsert({
where: { code: "LUBE-EP80W90" },
update: {},
create: {
code: "LUBE-EP80W90",
name: "Gear Oil EP 80W90",
description: "Eni EP 80W90 gear oil for marine gearboxes",
lastPrice: 182,
},
});
await db.product.upsert({
where: { code: "LUBE-HYD46" },
update: {},
create: {
code: "LUBE-HYD46",
name: "Hydraulic Oil ISO 46",
description: "Anti-wear hydraulic oil ISO VG 46 for deck machinery",
lastPrice: 155,
},
});
await db.product.upsert({
where: { code: "LUBE-MEO30" },
update: {},
create: {
code: "LUBE-MEO30",
name: "Main Engine Oil SAE 30",
description: "Trunk piston engine oil SAE 30 for 4-stroke engines",
lastPrice: 210,
},
});
await db.product.upsert({
where: { code: "FILT-OIL-ME" },
update: {},
create: {
code: "FILT-OIL-ME",
name: "Main Engine Lube Oil Filter",
description: "Spin-on lube oil filter for main engine",
lastPrice: 850,
},
});
await db.product.upsert({
where: { code: "FILT-FUEL-ME" },
update: {},
create: {
code: "FILT-FUEL-ME",
name: "Main Engine Fuel Filter",
description: "Duplex fuel oil filter element for main engine",
lastPrice: 1100,
},
});
await db.product.upsert({
where: { code: "FILT-AIR-ME" },
update: {},
create: {
code: "FILT-AIR-ME",
name: "Air Filter Element",
description: "Air cleaner filter element for main engine air intake",
lastPrice: 650,
},
});
await db.product.upsert({
where: { code: "PART-ORING-ASST" },
update: {},
create: {
code: "PART-ORING-ASST",
name: "O-Ring Assortment Pack",
description: "Mixed O-ring kit with Nitrile and Viton rings, 500 pcs",
lastPrice: 250,
},
});
await db.product.upsert({
where: { code: "PART-GASKET-SET" },
update: {},
create: {
code: "PART-GASKET-SET",
name: "Exhaust Gasket Set",
description: "Full set of exhaust manifold gaskets for main engine",
lastPrice: 3200,
},
});
await db.product.upsert({
where: { code: "ELEC-LAMP-LED" },
update: {},
create: {
code: "ELEC-LAMP-LED",
name: "LED Navigation Lamp",
description: "SOLAS-compliant LED navigation light, white masthead",
lastPrice: 4500,
},
});
await db.product.upsert({
where: { code: "ELEC-BATT-12V" },
update: {},
create: {
code: "ELEC-BATT-12V",
name: "Starting Battery 12V 100Ah",
description: "Sealed lead-acid starting battery for emergency equipment",
lastPrice: 6500,
},
});
await db.product.upsert({
where: { code: "ELEC-CABLE-3C" },
update: {},
create: {
code: "ELEC-CABLE-3C",
name: "Marine Cable 3-Core 2.5mm²",
description: "Tinned copper 3-core marine electrical cable, per metre",
lastPrice: 85,
},
});
await db.product.upsert({
where: { code: "ROPE-MOORING-40" },
update: {},
create: {
code: "ROPE-MOORING-40",
name: "Mooring Rope 40mm × 200m",
description: "3-strand polypropylene mooring rope, 40mm dia, 200m coil",
lastPrice: 18500,
},
});
await db.product.upsert({
where: { code: "ROPE-PILOT-18" },
update: {},
create: {
code: "ROPE-PILOT-18",
name: "Pilot Ladder Rope 18mm",
description: "Manilla pilot ladder rope, 18mm, certified per metre",
lastPrice: 320,
},
});
await db.product.upsert({
where: { code: "SAFE-IMMSUIT" },
update: {},
create: {
code: "SAFE-IMMSUIT",
name: "Immersion Suit (SOLAS)",
description: "SOLAS-approved adult immersion suit, insulated",
lastPrice: 5500,
},
});
await db.product.upsert({
where: { code: "SAFE-EPIRB" },
update: {},
create: {
code: "SAFE-EPIRB",
name: "EPIRB (406 MHz)",
description: "Category I float-free EPIRB, 406 MHz COSPAS-SARSAT",
lastPrice: 45000,
},
});
await db.product.upsert({
where: { code: "SAFE-FLARE-HAND" },
update: {},
create: {
code: "SAFE-FLARE-HAND",
name: "Hand Flare (SOLAS)",
description: "Red hand flare for distress signalling, pack of 6",
lastPrice: 1800,
},
});
await db.product.upsert({
where: { code: "PAINT-ANTIFOUL" },
update: {},
create: {
code: "PAINT-ANTIFOUL",
name: "Antifouling Paint 20L",
description: "Self-polishing antifouling paint for hull below waterline",
lastPrice: 8200,
},
});
await db.product.upsert({
where: { code: "PAINT-PRIMER" },
update: {},
create: {
code: "PAINT-PRIMER",
name: "Epoxy Primer 5L",
description: "Two-component epoxy primer for steel and aluminium surfaces",
lastPrice: 3400,
},
});
await db.product.upsert({
where: { code: "TOOL-GRINDER-4" },
update: {},
create: {
code: "TOOL-GRINDER-4",
name: "Angle Grinder 4-inch",
description: "Heavy-duty 4-inch angle grinder, 850W, with guard",
lastPrice: 2800,
},
});
await db.product.upsert({
where: { code: "CHART-INT-1" },
update: {},
create: {
code: "CHART-INT-1",
name: "INT Chart Folio Update",
description: "Annual update pack for navigational charts, Indian Ocean folio",
lastPrice: 950,
},
});
await db.product.upsert({
where: { code: "CHEM-BOWTREATER" },
update: {},
create: {
code: "CHEM-BOWTREATER",
name: "Boiler Water Treatment Chemical 25L",
description: "Liquid boiler water treatment and scale inhibitor",
lastPrice: 3600,
},
});
// Sample POs — upsert so re-running seed is safe
const po1 = await db.purchaseOrder.upsert({
where: { poNumber: "PO-2026-00001" },
update: { currency: "INR", totalAmount: 9971.0 },
create: {
poNumber: "PO-2026-00001",
title: "Engine Room Spare Parts — MV Pelagia Star",
status: "MGR_REVIEW",
totalAmount: 9971.0, // 8450 taxable × 1.18 GST
currency: "INR",
submittedAt: new Date(),
submitterId: technical.id,
vesselId: mv1.id,
accountId: acc1.id,
vendorId: vendor1.id,
lineItems: {
create: [
{ name: "Turbocharger seal kit", quantity: 2, unit: "set", unitPrice: 1200, totalPrice: 2400, sortOrder: 0, productId: prod1.id },
{ name: "High-pressure fuel pump", quantity: 1, unit: "pc", unitPrice: 4800, totalPrice: 4800, sortOrder: 1, productId: prod2.id },
{ name: "O-ring assortment pack", quantity: 5, unit: "pk", unitPrice: 250, totalPrice: 1250, sortOrder: 2 },
],
},
actions: {
create: [
{ actionType: "CREATED", actorId: technical.id },
{ actionType: "SUBMITTED", actorId: technical.id },
],
},
},
});
await db.purchaseOrder.upsert({
where: { poNumber: "PO-2026-00002" },
update: { currency: "INR", totalAmount: 3776.0 },
create: {
poNumber: "PO-2026-00002",
title: "Crew Safety Equipment — MV Aegean Wind",
status: "DRAFT",
totalAmount: 3776.0, // 3200 taxable × 1.18 GST
currency: "INR",
submitterId: technical.id,
vesselId: mv2.id,
accountId: acc2.id,
lineItems: {
create: [
{ name: "Life jackets (SOLAS)", quantity: 20, unit: "pc", unitPrice: 120, totalPrice: 2400, sortOrder: 0 },
{ name: "Fire extinguisher — 9kg", quantity: 4, unit: "pc", unitPrice: 200, totalPrice: 800, sortOrder: 1 },
],
},
actions: {
create: [{ actionType: "CREATED", actorId: technical.id }],
},
},
});
await db.purchaseOrder.upsert({
where: { poNumber: "PO-2026-00003" },
update: { currency: "INR", totalAmount: 1121.0 },
create: {
poNumber: "PO-2026-00003",
title: "Navigation Charts Update — Fleet",
status: "MGR_APPROVED",
totalAmount: 1121.0, // 950 taxable × 1.18 GST
currency: "INR",
submittedAt: new Date(Date.now() - 5 * 86400000),
approvedAt: new Date(Date.now() - 2 * 86400000),
submitterId: technical.id,
vesselId: mv1.id,
accountId: acc1.id,
lineItems: {
create: [
{ name: "INT chart folio update", quantity: 1, unit: "set", unitPrice: 950, totalPrice: 950, sortOrder: 0 },
],
},
actions: {
create: [
{ actionType: "CREATED", actorId: technical.id },
{ actionType: "SUBMITTED", actorId: technical.id },
{ actionType: "APPROVED", actorId: manager.id, note: "Approved — update due before next voyage." },
],
},
},
});
console.log("Seed complete.");
console.log("\nDev login credentials:");
console.log(" Admin: admin@pelagia.local / admin1234");
console.log(" Manager: manager@pelagia.local / manager1234");
console.log(" Tech: tech@pelagia.local / tech1234");
console.log(" Accounts: accounts@pelagia.local / accounts1234");
console.log(" Manning: manning@pelagia.local / manning1234");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => db.$disconnect());