uup
This commit is contained in:
157
prisma/CAR_DATASET_README.md
Normal file
157
prisma/CAR_DATASET_README.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Car Dataset System
|
||||
|
||||
## Overview
|
||||
The Car Dataset system provides a structured way to manage vehicle manufacturers, models, and body types. This ensures data consistency and improves user experience when adding new vehicles.
|
||||
|
||||
## Database Schema
|
||||
|
||||
### CarDataset Table
|
||||
- `id`: Primary key (auto-increment)
|
||||
- `manufacturer`: Vehicle manufacturer name (e.g., "Toyota", "Honda")
|
||||
- `model`: Vehicle model name (e.g., "Camry", "Civic")
|
||||
- `bodyType`: Vehicle body type (e.g., "Sedan", "SUV", "Hatchback")
|
||||
- `isActive`: Boolean flag to enable/disable entries
|
||||
- `createdDate`: Record creation timestamp
|
||||
- `updateDate`: Record last update timestamp
|
||||
|
||||
### Unique Constraint
|
||||
- Combination of `manufacturer` and `model` must be unique
|
||||
- This prevents duplicate entries for the same car model
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Vehicle Form Enhancement
|
||||
The vehicle form now uses autocomplete inputs for:
|
||||
- **Manufacturer Selection**: Users type to search from available manufacturers
|
||||
- **Model Selection**: After selecting a manufacturer, users can search models for that manufacturer
|
||||
- **Body Type Auto-Fill**: When a model is selected, the body type is automatically filled from the dataset
|
||||
|
||||
### 2. API Endpoints
|
||||
- `GET /api/car-dataset?action=manufacturers` - Returns all unique manufacturers
|
||||
- `GET /api/car-dataset?action=models&manufacturer=Toyota` - Returns models for a specific manufacturer
|
||||
- `GET /api/car-dataset?action=bodyType&manufacturer=Toyota&model=Camry` - Returns body type for a specific model
|
||||
|
||||
### 3. User Experience Flow
|
||||
1. User enters plate number
|
||||
2. User starts typing manufacturer name → autocomplete shows matching manufacturers
|
||||
3. User selects manufacturer → model field becomes enabled
|
||||
4. User starts typing model name → autocomplete shows models for selected manufacturer
|
||||
5. User selects model → body type is automatically filled
|
||||
6. User continues with other vehicle details (year, transmission, etc.)
|
||||
|
||||
## Seeding Data
|
||||
|
||||
### Initial Dataset
|
||||
The system comes pre-loaded with popular car manufacturers and models:
|
||||
- Toyota (10 models)
|
||||
- Honda (8 models)
|
||||
- Nissan (9 models)
|
||||
- Hyundai (8 models)
|
||||
- Kia (8 models)
|
||||
- Ford (8 models)
|
||||
- Chevrolet (8 models)
|
||||
- BMW (8 models)
|
||||
- Mercedes-Benz (8 models)
|
||||
- Audi (8 models)
|
||||
- Lexus (8 models)
|
||||
- Mazda (7 models)
|
||||
- Mitsubishi (5 models)
|
||||
- Subaru (6 models)
|
||||
- Volkswagen (6 models)
|
||||
- Infiniti (5 models)
|
||||
- Acura (5 models)
|
||||
|
||||
### Running the Seed
|
||||
```bash
|
||||
# Seed the car dataset
|
||||
npx tsx prisma/carDatasetSeed.ts
|
||||
```
|
||||
|
||||
## Management Functions
|
||||
|
||||
### Server Functions (`app/lib/car-dataset-management.server.ts`)
|
||||
- `getManufacturers()` - Get all unique manufacturers
|
||||
- `getModelsByManufacturer(manufacturer)` - Get models for a manufacturer
|
||||
- `getBodyType(manufacturer, model)` - Get body type for a specific model
|
||||
- `createCarDataset(data)` - Add new car dataset entry
|
||||
- `updateCarDataset(id, data)` - Update existing entry
|
||||
- `deleteCarDataset(id)` - Delete entry
|
||||
- `bulkImportCarDataset(data[])` - Bulk import multiple entries
|
||||
|
||||
### Adding New Cars
|
||||
To add new cars to the dataset, you can:
|
||||
1. Use the bulk import function with an array of car data
|
||||
2. Create individual entries using the create function
|
||||
3. Manually insert into the database
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
await createCarDataset({
|
||||
manufacturer: "Tesla",
|
||||
model: "Model 3",
|
||||
bodyType: "Sedan"
|
||||
});
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
### 1. Data Consistency
|
||||
- Standardized manufacturer and model names
|
||||
- Consistent body type classifications
|
||||
- Prevents typos and variations in naming
|
||||
|
||||
### 2. Improved User Experience
|
||||
- Fast autocomplete search
|
||||
- Reduced typing for common vehicles
|
||||
- Automatic body type detection
|
||||
|
||||
### 3. Maintenance
|
||||
- Easy to add new manufacturers and models
|
||||
- Centralized vehicle data management
|
||||
- Can disable outdated models without deleting data
|
||||
|
||||
### 4. Reporting
|
||||
- Accurate statistics by manufacturer
|
||||
- Consistent data for analytics
|
||||
- Better search and filtering capabilities
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 1. Database Migration
|
||||
```bash
|
||||
# Apply the schema changes
|
||||
npx prisma db push
|
||||
|
||||
# Or run the SQL migration directly
|
||||
sqlite3 prisma/dev.db < prisma/migrations/add_car_dataset.sql
|
||||
```
|
||||
|
||||
### 2. Generate Prisma Client
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### 3. Seed the Dataset
|
||||
```bash
|
||||
npx tsx prisma/carDatasetSeed.ts
|
||||
```
|
||||
|
||||
### 4. Update Application
|
||||
The vehicle form will automatically use the new car dataset system once the migration is complete.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Possible Additions
|
||||
- Vehicle trim levels per model
|
||||
- Engine specifications per model
|
||||
- Year ranges for model availability
|
||||
- Regional model variations
|
||||
- Integration with external vehicle databases
|
||||
- Admin interface for managing car dataset
|
||||
|
||||
### API Extensions
|
||||
- Search across all fields
|
||||
- Pagination for large datasets
|
||||
- Filtering by body type or year
|
||||
- Export/import functionality
|
||||
- Validation against external sources
|
||||
141
prisma/MAINTENANCE_TYPES_README.md
Normal file
141
prisma/MAINTENANCE_TYPES_README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Maintenance Types Seeding
|
||||
|
||||
This directory contains a dedicated seed file for populating the maintenance types table with comprehensive Arabic maintenance service types.
|
||||
|
||||
## Files
|
||||
|
||||
- `maintenanceTypeSeed.ts` - Standalone seed file for maintenance types
|
||||
- `MAINTENANCE_TYPES_README.md` - This documentation file
|
||||
|
||||
## Usage
|
||||
|
||||
### Run the maintenance types seed
|
||||
|
||||
```bash
|
||||
npm run db:seed:maintenance-types
|
||||
```
|
||||
|
||||
Or run directly with tsx:
|
||||
|
||||
```bash
|
||||
npx tsx prisma/maintenanceTypeSeed.ts
|
||||
```
|
||||
|
||||
### What it does
|
||||
|
||||
The seed file will:
|
||||
1. Create new maintenance types that don't exist
|
||||
2. Update existing maintenance types with new descriptions
|
||||
3. Preserve existing data (no deletions)
|
||||
4. Show a summary of created/updated types
|
||||
5. Display all maintenance types in the database
|
||||
|
||||
## Maintenance Types Included
|
||||
|
||||
The seed includes 20 comprehensive maintenance types in Arabic:
|
||||
|
||||
### Basic Maintenance
|
||||
- **صيانة دورية** - Comprehensive periodic maintenance
|
||||
- **تغيير زيت المحرك** - Engine oil and filter change
|
||||
- **فحص دوري** - Periodic inspection
|
||||
- **تنظيف شامل** - Complete cleaning
|
||||
|
||||
### Engine & Transmission
|
||||
- **إصلاح المحرك** - Engine repair and maintenance
|
||||
- **إصلاح ناقل الحركة** - Transmission repair
|
||||
- **إصلاح الرادياتير** - Cooling system and radiator repair
|
||||
|
||||
### Brakes & Suspension
|
||||
- **إصلاح الفرامل** - Brake system repair
|
||||
- **إصلاح التعليق** - Suspension system repair
|
||||
- **إصلاح الإطارات** - Tire repair and replacement
|
||||
|
||||
### Electrical & Electronics
|
||||
- **إصلاح الكهرباء** - Electrical system repair
|
||||
- **إصلاح البطارية** - Battery and charging system
|
||||
- **إصلاح المصابيح** - Lighting system repair
|
||||
|
||||
### Body & Interior
|
||||
- **إصلاح الهيكل** - Body and frame repair
|
||||
- **إصلاح الزجاج** - Glass repair and replacement
|
||||
- **إصلاح التكييف** - AC and climate control
|
||||
|
||||
### Exhaust & Other
|
||||
- **إصلاح العادم** - Exhaust system repair
|
||||
- **فحص ما قبل السفر** - Pre-travel inspection
|
||||
- **صيانة طارئة** - Emergency maintenance
|
||||
- **أخرى** - Other maintenance types
|
||||
|
||||
## Features
|
||||
|
||||
### Smart Upsert Logic
|
||||
- Creates new maintenance types if they don't exist
|
||||
- Updates existing types with new descriptions
|
||||
- Preserves custom maintenance types added by users
|
||||
|
||||
### Detailed Descriptions
|
||||
Each maintenance type includes:
|
||||
- Arabic name
|
||||
- Detailed description of what's included
|
||||
- Active status (all default to active)
|
||||
|
||||
### Safe Operation
|
||||
- No data deletion
|
||||
- Preserves existing maintenance visits
|
||||
- Can be run multiple times safely
|
||||
|
||||
### Comprehensive Logging
|
||||
- Shows progress for each maintenance type
|
||||
- Displays summary statistics
|
||||
- Lists all maintenance types in the database
|
||||
|
||||
## Customization
|
||||
|
||||
To add more maintenance types, edit the `maintenanceTypes` array in `maintenanceTypeSeed.ts`:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'نوع الصيانة الجديد',
|
||||
description: 'وصف تفصيلي لنوع الصيانة',
|
||||
isActive: true,
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Main Seed
|
||||
|
||||
The maintenance types seed is also integrated into the main seed file (`seed.ts`), so running `npm run db:seed` will also populate maintenance types.
|
||||
|
||||
## Database Schema
|
||||
|
||||
The maintenance types are stored in the `maintenance_types` table with:
|
||||
- `id` - Auto-increment primary key
|
||||
- `name` - Unique maintenance type name
|
||||
- `description` - Optional detailed description
|
||||
- `isActive` - Boolean flag for active/inactive status
|
||||
- `createdDate` - Creation timestamp
|
||||
- `updateDate` - Last update timestamp
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Permission Errors
|
||||
Make sure your database connection has write permissions.
|
||||
|
||||
### Duplicate Name Errors
|
||||
The seed handles duplicates gracefully by updating existing records.
|
||||
|
||||
### TypeScript Errors
|
||||
Ensure all dependencies are installed:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Database Connection Issues
|
||||
Check your `.env` file has the correct `DATABASE_URL`.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Add maintenance type categories
|
||||
- Include pricing suggestions
|
||||
- Add maintenance intervals
|
||||
- Support for multiple languages
|
||||
- Import/export functionality
|
||||
1448
prisma/carDatasetSeed.ts
Normal file
1448
prisma/carDatasetSeed.ts
Normal file
File diff suppressed because it is too large
Load Diff
0
prisma/dev.db
Normal file
0
prisma/dev.db
Normal file
264
prisma/maintenanceTypeSeed.ts
Normal file
264
prisma/maintenanceTypeSeed.ts
Normal file
@@ -0,0 +1,264 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const maintenanceTypes = [
|
||||
{
|
||||
name: 'صيانة دورية',
|
||||
description: 'صيانة دورية شاملة للمركبة تشمل فحص جميع الأنظمة الأساسية',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'تغيير زيت المحرك',
|
||||
description: 'تغيير زيت المحرك وفلتر الزيت وفحص مستوى السوائل',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الفرامل',
|
||||
description: 'صيانة وإصلاح نظام الفرامل بما في ذلك الأقراص والتيل والسوائل',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح المحرك',
|
||||
description: 'إصلاح وصيانة المحرك وأجزائه الداخلية والخارجية',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح ناقل الحركة',
|
||||
description: 'صيانة وإصلاح ناقل الحركة الأوتوماتيكي أو اليدوي',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح التكييف',
|
||||
description: 'صيانة وإصلاح نظام التكييف والتبريد في المركبة',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الإطارات',
|
||||
description: 'تغيير وإصلاح الإطارات وضبط الهواء والتوازن',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الكهرباء',
|
||||
description: 'إصلاح الأنظمة الكهربائية والإلكترونية في المركبة',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح التعليق',
|
||||
description: 'صيانة وإصلاح نظام التعليق والممتصات',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح العادم',
|
||||
description: 'إصلاح وتغيير نظام العادم والكاتم',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الرادياتير',
|
||||
description: 'صيانة وإصلاح نظام التبريد والرادياتير',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح البطارية',
|
||||
description: 'فحص وتغيير البطارية ونظام الشحن',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح المصابيح',
|
||||
description: 'إصلاح وتغيير المصابيح الأمامية والخلفية',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الزجاج',
|
||||
description: 'إصلاح وتغيير الزجاج الأمامي والخلفي والجانبي',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'إصلاح الهيكل',
|
||||
description: 'إصلاح أضرار الهيكل والصدمات والخدوش',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'تنظيف شامل',
|
||||
description: 'تنظيف شامل للمركبة من الداخل والخارج',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'فحص دوري',
|
||||
description: 'فحص دوري شامل لجميع أنظمة المركبة',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'فحص ما قبل السفر',
|
||||
description: 'فحص شامل للمركبة قبل السفر الطويل',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'صيانة طارئة',
|
||||
description: 'صيانة طارئة لحل مشاكل عاجلة في المركبة',
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
name: 'أخرى',
|
||||
description: 'أنواع صيانة أخرى غير مدرجة في القائمة',
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
async function seedMaintenanceTypes() {
|
||||
console.log('🔧 Seeding maintenance types...');
|
||||
|
||||
try {
|
||||
// Check if there are any maintenance visits that might reference maintenance types in JSON
|
||||
const visitCount = await prisma.maintenanceVisit.count();
|
||||
|
||||
if (visitCount > 0) {
|
||||
console.log(`⚠️ Found ${visitCount} maintenance visits in database.`);
|
||||
console.log('🔄 Analyzing maintenance jobs in existing visits...');
|
||||
|
||||
// Get all maintenance visits to check their JSON maintenance jobs
|
||||
const visits = await prisma.maintenanceVisit.findMany({
|
||||
select: { maintenanceJobs: true },
|
||||
});
|
||||
|
||||
const referencedTypeIds = new Set<number>();
|
||||
|
||||
// Parse JSON maintenance jobs to find referenced type IDs
|
||||
visits.forEach(visit => {
|
||||
try {
|
||||
const jobs = JSON.parse(visit.maintenanceJobs);
|
||||
if (Array.isArray(jobs)) {
|
||||
jobs.forEach(job => {
|
||||
if (job.typeId && typeof job.typeId === 'number') {
|
||||
referencedTypeIds.add(job.typeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Skip invalid JSON
|
||||
}
|
||||
});
|
||||
|
||||
if (referencedTypeIds.size > 0) {
|
||||
console.log(`📋 Found ${referencedTypeIds.size} maintenance type IDs referenced in visits`);
|
||||
|
||||
// Delete only maintenance types that are NOT referenced
|
||||
const deletedTypes = await prisma.maintenanceType.deleteMany({
|
||||
where: {
|
||||
id: {
|
||||
notIn: Array.from(referencedTypeIds),
|
||||
},
|
||||
},
|
||||
});
|
||||
console.log(`🗑️ Deleted ${deletedTypes.count} unreferenced maintenance types`);
|
||||
|
||||
// Update referenced maintenance types to match our seed data
|
||||
console.log('🔄 Updating referenced maintenance types...');
|
||||
for (const type of maintenanceTypes) {
|
||||
const existingType = await prisma.maintenanceType.findUnique({
|
||||
where: { name: type.name },
|
||||
});
|
||||
|
||||
if (existingType && referencedTypeIds.has(existingType.id)) {
|
||||
await prisma.maintenanceType.update({
|
||||
where: { id: existingType.id },
|
||||
data: {
|
||||
description: type.description,
|
||||
isActive: type.isActive,
|
||||
},
|
||||
});
|
||||
console.log(`✅ Updated referenced type: ${type.name}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No maintenance types are actually referenced, safe to delete all
|
||||
const deletedCount = await prisma.maintenanceType.deleteMany();
|
||||
console.log(`🗑️ Deleted ${deletedCount.count} maintenance types`);
|
||||
}
|
||||
} else {
|
||||
// No maintenance visits exist, safe to delete all maintenance types
|
||||
const deletedCount = await prisma.maintenanceType.deleteMany();
|
||||
console.log(`🗑️ Deleted ${deletedCount.count} existing maintenance types`);
|
||||
}
|
||||
|
||||
// Reset the auto-increment counter for SQLite
|
||||
console.log('🔄 Resetting ID counter...');
|
||||
await prisma.$executeRaw`DELETE FROM sqlite_sequence WHERE name = 'maintenance_types'`;
|
||||
console.log('✅ ID counter reset to start from 1');
|
||||
|
||||
console.log('📝 Inserting fresh maintenance types...');
|
||||
|
||||
let createdCount = 0;
|
||||
let updatedCount = 0;
|
||||
|
||||
for (const type of maintenanceTypes) {
|
||||
try {
|
||||
console.log(`Processing: ${type.name}`);
|
||||
|
||||
const result = await prisma.maintenanceType.upsert({
|
||||
where: { name: type.name },
|
||||
update: {
|
||||
description: type.description,
|
||||
isActive: type.isActive,
|
||||
},
|
||||
create: {
|
||||
name: type.name,
|
||||
description: type.description,
|
||||
isActive: type.isActive,
|
||||
},
|
||||
});
|
||||
|
||||
if (result.createdDate.getTime() === result.updateDate.getTime()) {
|
||||
createdCount++;
|
||||
console.log(`✅ Created: ${type.name} (ID: ${result.id})`);
|
||||
} else {
|
||||
updatedCount++;
|
||||
console.log(`✅ Updated: ${type.name} (ID: ${result.id})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Error processing "${type.name}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n📊 Summary:`);
|
||||
console.log(` Created: ${createdCount} maintenance types`);
|
||||
console.log(` Updated: ${updatedCount} maintenance types`);
|
||||
console.log(` Total: ${maintenanceTypes.length} maintenance types processed`);
|
||||
|
||||
// Display all maintenance types
|
||||
const allTypes = await prisma.maintenanceType.findMany({
|
||||
orderBy: { name: 'asc' },
|
||||
});
|
||||
|
||||
console.log(`\n📋 All maintenance types in database (${allTypes.length}):`);
|
||||
allTypes.forEach((type, index) => {
|
||||
const status = type.isActive ? '🟢' : '🔴';
|
||||
console.log(` ${index + 1}. ${status} ${type.name}`);
|
||||
if (type.description) {
|
||||
console.log(` 📝 ${type.description}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n🎉 Maintenance types seeding completed successfully!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error during maintenance types seeding:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await seedMaintenanceTypes();
|
||||
} catch (error) {
|
||||
console.error('❌ Error seeding maintenance types:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Always run the main function when this file is executed
|
||||
main();
|
||||
|
||||
export { seedMaintenanceTypes };
|
||||
94
prisma/migrations/20250812120412_init/migration.sql
Normal file
94
prisma/migrations/20250812120412_init/migration.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL DEFAULT 'active',
|
||||
"authLevel" INTEGER NOT NULL,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"editDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "customers" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"phone" TEXT,
|
||||
"email" TEXT,
|
||||
"address" TEXT,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "vehicles" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"plateNumber" TEXT NOT NULL,
|
||||
"bodyType" TEXT NOT NULL,
|
||||
"manufacturer" TEXT NOT NULL,
|
||||
"model" TEXT NOT NULL,
|
||||
"trim" TEXT,
|
||||
"year" INTEGER NOT NULL,
|
||||
"transmission" TEXT NOT NULL,
|
||||
"fuel" TEXT NOT NULL,
|
||||
"cylinders" INTEGER,
|
||||
"engineDisplacement" REAL,
|
||||
"useType" TEXT NOT NULL,
|
||||
"ownerId" INTEGER NOT NULL,
|
||||
"lastVisitDate" DATETIME,
|
||||
"suggestedNextVisitDate" DATETIME,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "vehicles_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "customers" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "maintenance_visits" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"vehicleId" INTEGER NOT NULL,
|
||||
"customerId" INTEGER NOT NULL,
|
||||
"maintenanceType" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"cost" REAL NOT NULL,
|
||||
"paymentStatus" TEXT NOT NULL DEFAULT 'pending',
|
||||
"kilometers" INTEGER NOT NULL,
|
||||
"visitDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"nextVisitDelay" INTEGER NOT NULL,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "maintenance_visits_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "vehicles" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "maintenance_visits_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "customers" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "expenses" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"description" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL,
|
||||
"amount" REAL NOT NULL,
|
||||
"expenseDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "income" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"maintenanceVisitId" INTEGER NOT NULL,
|
||||
"amount" REAL NOT NULL,
|
||||
"incomeDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "income_maintenanceVisitId_fkey" FOREIGN KEY ("maintenanceVisitId") REFERENCES "maintenance_visits" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "vehicles_plateNumber_key" ON "vehicles"("plateNumber");
|
||||
45
prisma/migrations/20250827142342_newdis/migration.sql
Normal file
45
prisma/migrations/20250827142342_newdis/migration.sql
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `maintenanceType` on the `maintenance_visits` table. All the data in the column will be lost.
|
||||
- Added the required column `maintenanceTypeId` to the `maintenance_visits` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- CreateTable
|
||||
CREATE TABLE "maintenance_types" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_maintenance_visits" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"vehicleId" INTEGER NOT NULL,
|
||||
"customerId" INTEGER NOT NULL,
|
||||
"maintenanceTypeId" INTEGER NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"cost" REAL NOT NULL,
|
||||
"paymentStatus" TEXT NOT NULL DEFAULT 'pending',
|
||||
"kilometers" INTEGER NOT NULL,
|
||||
"visitDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"nextVisitDelay" INTEGER NOT NULL,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "maintenance_visits_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "vehicles" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "maintenance_visits_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "customers" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "maintenance_visits_maintenanceTypeId_fkey" FOREIGN KEY ("maintenanceTypeId") REFERENCES "maintenance_types" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_maintenance_visits" ("cost", "createdDate", "customerId", "description", "id", "kilometers", "nextVisitDelay", "paymentStatus", "updateDate", "vehicleId", "visitDate") SELECT "cost", "createdDate", "customerId", "description", "id", "kilometers", "nextVisitDelay", "paymentStatus", "updateDate", "vehicleId", "visitDate" FROM "maintenance_visits";
|
||||
DROP TABLE "maintenance_visits";
|
||||
ALTER TABLE "new_maintenance_visits" RENAME TO "maintenance_visits";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "maintenance_types_name_key" ON "maintenance_types"("name");
|
||||
31
prisma/migrations/20250827220800_newdis2/migration.sql
Normal file
31
prisma/migrations/20250827220800_newdis2/migration.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `maintenanceTypeId` on the `maintenance_visits` table. All the data in the column will be lost.
|
||||
- Added the required column `maintenanceJobs` to the `maintenance_visits` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_maintenance_visits" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"vehicleId" INTEGER NOT NULL,
|
||||
"customerId" INTEGER NOT NULL,
|
||||
"maintenanceJobs" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"cost" REAL NOT NULL,
|
||||
"paymentStatus" TEXT NOT NULL DEFAULT 'pending',
|
||||
"kilometers" INTEGER NOT NULL,
|
||||
"visitDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"nextVisitDelay" INTEGER NOT NULL,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "maintenance_visits_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "vehicles" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "maintenance_visits_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "customers" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_maintenance_visits" ("cost", "createdDate", "customerId", "description", "id", "kilometers", "nextVisitDelay", "paymentStatus", "updateDate", "vehicleId", "visitDate") SELECT "cost", "createdDate", "customerId", "description", "id", "kilometers", "nextVisitDelay", "paymentStatus", "updateDate", "vehicleId", "visitDate" FROM "maintenance_visits";
|
||||
DROP TABLE "maintenance_visits";
|
||||
ALTER TABLE "new_maintenance_visits" RENAME TO "maintenance_visits";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
13
prisma/migrations/add_car_dataset.sql
Normal file
13
prisma/migrations/add_car_dataset.sql
Normal file
@@ -0,0 +1,13 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "car_dataset" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"manufacturer" TEXT NOT NULL,
|
||||
"model" TEXT NOT NULL,
|
||||
"bodyType" TEXT NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "car_dataset_manufacturer_model_key" ON "car_dataset"("manufacturer", "model");
|
||||
59
prisma/migrations/add_maintenance_types.sql
Normal file
59
prisma/migrations/add_maintenance_types.sql
Normal file
@@ -0,0 +1,59 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "maintenance_types" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "maintenance_types_name_key" ON "maintenance_types"("name");
|
||||
|
||||
-- Add new column to maintenance_visits for JSON maintenance jobs
|
||||
ALTER TABLE "maintenance_visits" ADD COLUMN "maintenanceJobs" TEXT;
|
||||
|
||||
-- Create default maintenance types (will be populated by seed script)
|
||||
-- Note: The seed script will handle populating maintenance types
|
||||
|
||||
-- Convert existing maintenance visits to use JSON format
|
||||
-- First, let's handle the case where maintenanceType column exists (old format)
|
||||
UPDATE "maintenance_visits"
|
||||
SET "maintenanceJobs" = '[{"typeId": 1, "job": "' || COALESCE("maintenanceType", 'صيانة عامة') || '", "notes": ""}]'
|
||||
WHERE "maintenanceJobs" IS NULL;
|
||||
|
||||
-- Set default JSON for any remaining NULL values
|
||||
UPDATE "maintenance_visits"
|
||||
SET "maintenanceJobs" = '[{"typeId": 1, "job": "صيانة عامة", "notes": ""}]'
|
||||
WHERE "maintenanceJobs" IS NULL OR "maintenanceJobs" = '';
|
||||
|
||||
-- Make maintenanceJobs NOT NULL and remove old maintenanceType column if it exists
|
||||
-- Note: SQLite doesn't support ALTER COLUMN, so we need to recreate the table
|
||||
CREATE TABLE "maintenance_visits_new" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"vehicleId" INTEGER NOT NULL,
|
||||
"customerId" INTEGER NOT NULL,
|
||||
"maintenanceJobs" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"cost" REAL NOT NULL,
|
||||
"paymentStatus" TEXT NOT NULL DEFAULT 'pending',
|
||||
"kilometers" INTEGER NOT NULL,
|
||||
"visitDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"nextVisitDelay" INTEGER NOT NULL,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL,
|
||||
CONSTRAINT "maintenance_visits_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "vehicles" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "maintenance_visits_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "customers" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- Copy data from old table to new table (excluding old maintenanceType column)
|
||||
INSERT INTO "maintenance_visits_new"
|
||||
SELECT "id", "vehicleId", "customerId", "maintenanceJobs", "description", "cost", "paymentStatus", "kilometers", "visitDate", "nextVisitDelay", "createdDate", "updateDate"
|
||||
FROM "maintenance_visits";
|
||||
|
||||
-- Drop old table and rename new table
|
||||
DROP TABLE "maintenance_visits";
|
||||
ALTER TABLE "maintenance_visits_new" RENAME TO "maintenance_visits";
|
||||
|
||||
-- Note: Income table foreign key constraint should still work as it references maintenance_visits(id)
|
||||
19
prisma/migrations/add_settings.sql
Normal file
19
prisma/migrations/add_settings.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- Migration: Add Settings Table
|
||||
-- Description: Creates settings table for application configuration (date format, currency, number format)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "settings" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"key" TEXT NOT NULL UNIQUE,
|
||||
"value" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"createdDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Insert default settings
|
||||
INSERT OR IGNORE INTO "settings" ("key", "value", "description") VALUES
|
||||
('dateFormat', 'ar-SA', 'Date format locale (ar-SA or en-US)'),
|
||||
('currency', 'JOD', 'Currency code (JOD, USD, EUR, etc.)'),
|
||||
('numberFormat', 'ar-SA', 'Number format locale (ar-SA or en-US)'),
|
||||
('currencySymbol', 'د.أ', 'Currency symbol display'),
|
||||
('dateDisplayFormat', 'dd/MM/yyyy', 'Date display format pattern');
|
||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "sqlite"
|
||||
BIN
prisma/prisma/dev.db
Normal file
BIN
prisma/prisma/dev.db
Normal file
Binary file not shown.
160
prisma/schema.prisma
Normal file
160
prisma/schema.prisma
Normal file
@@ -0,0 +1,160 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// User model for authentication and access control
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
username String @unique
|
||||
email String @unique
|
||||
password String // hashed password
|
||||
status String @default("active") // "active" or "inactive"
|
||||
authLevel Int // 1=superadmin, 2=admin, 3=user
|
||||
createdDate DateTime @default(now())
|
||||
editDate DateTime @updatedAt
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
// Customer model for vehicle owners
|
||||
model Customer {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
phone String?
|
||||
email String?
|
||||
address String?
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
// Relationships
|
||||
vehicles Vehicle[]
|
||||
maintenanceVisits MaintenanceVisit[]
|
||||
|
||||
@@map("customers")
|
||||
}
|
||||
|
||||
// Vehicle model with comprehensive specifications
|
||||
model Vehicle {
|
||||
id Int @id @default(autoincrement())
|
||||
plateNumber String @unique
|
||||
bodyType String
|
||||
manufacturer String
|
||||
model String
|
||||
trim String?
|
||||
year Int
|
||||
transmission String // "Automatic" or "Manual"
|
||||
fuel String // "Gasoline", "Diesel", "Hybrid", "Mild Hybrid", "Electric"
|
||||
cylinders Int?
|
||||
engineDisplacement Float?
|
||||
useType String // "personal", "taxi", "apps", "loading", "travel"
|
||||
ownerId Int
|
||||
lastVisitDate DateTime?
|
||||
suggestedNextVisitDate DateTime?
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
// Relationships
|
||||
owner Customer @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
maintenanceVisits MaintenanceVisit[]
|
||||
|
||||
@@map("vehicles")
|
||||
}
|
||||
|
||||
// Maintenance type model for categorizing maintenance services
|
||||
model MaintenanceType {
|
||||
id Int @id @default(autoincrement())
|
||||
name String @unique
|
||||
description String?
|
||||
isActive Boolean @default(true)
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
@@map("maintenance_types")
|
||||
}
|
||||
|
||||
// Car dataset model for storing vehicle manufacturers, models, and body types
|
||||
model CarDataset {
|
||||
id Int @id @default(autoincrement())
|
||||
manufacturer String
|
||||
model String
|
||||
bodyType String
|
||||
isActive Boolean @default(true)
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
// Unique constraint to prevent duplicate manufacturer-model combinations
|
||||
@@unique([manufacturer, model])
|
||||
@@map("car_dataset")
|
||||
}
|
||||
|
||||
// Maintenance visit model for tracking service records
|
||||
model MaintenanceVisit {
|
||||
id Int @id @default(autoincrement())
|
||||
vehicleId Int
|
||||
customerId Int
|
||||
maintenanceJobs String // JSON field storing array of maintenance jobs: [{"typeId": 1, "job": "تغيير زيت المحرك", "notes": "..."}, ...]
|
||||
description String
|
||||
cost Float
|
||||
paymentStatus String @default("pending")
|
||||
kilometers Int
|
||||
visitDate DateTime @default(now())
|
||||
nextVisitDelay Int // months (1, 2, 3, or 4)
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
// Relationships
|
||||
vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
||||
income Income[]
|
||||
|
||||
@@map("maintenance_visits")
|
||||
}
|
||||
|
||||
// Expense model for business expense tracking
|
||||
model Expense {
|
||||
id Int @id @default(autoincrement())
|
||||
description String
|
||||
category String
|
||||
amount Float
|
||||
expenseDate DateTime @default(now())
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
@@map("expenses")
|
||||
}
|
||||
|
||||
// Income model for revenue tracking from maintenance visits
|
||||
model Income {
|
||||
id Int @id @default(autoincrement())
|
||||
maintenanceVisitId Int
|
||||
amount Float
|
||||
incomeDate DateTime @default(now())
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
// Relationships
|
||||
maintenanceVisit MaintenanceVisit @relation(fields: [maintenanceVisitId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("income")
|
||||
}
|
||||
|
||||
// Settings model for application configuration
|
||||
model Settings {
|
||||
id Int @id @default(autoincrement())
|
||||
key String @unique
|
||||
value String
|
||||
description String?
|
||||
createdDate DateTime @default(now())
|
||||
updateDate DateTime @updatedAt
|
||||
|
||||
@@map("settings")
|
||||
}
|
||||
168
prisma/seed.ts
Normal file
168
prisma/seed.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { seedMaintenanceTypes } from './maintenanceTypeSeed';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding database...');
|
||||
|
||||
// Check if superadmin already exists
|
||||
const existingSuperadmin = await prisma.user.findFirst({
|
||||
where: { authLevel: 1 }
|
||||
});
|
||||
|
||||
if (!existingSuperadmin) {
|
||||
// Create superadmin account
|
||||
const hashedPassword = await bcrypt.hash('admin123', 10);
|
||||
|
||||
const superadmin = await prisma.user.create({
|
||||
data: {
|
||||
name: 'Super Administrator',
|
||||
username: 'superadmin',
|
||||
email: 'admin@carmaintenance.com',
|
||||
password: hashedPassword,
|
||||
status: 'active',
|
||||
authLevel: 1,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created superadmin user:', superadmin.username);
|
||||
} else {
|
||||
console.log('ℹ️ Superadmin user already exists');
|
||||
}
|
||||
|
||||
// Seed maintenance types using the dedicated seed function
|
||||
await seedMaintenanceTypes();
|
||||
|
||||
// Seed some sample data for development
|
||||
const sampleCustomer = await prisma.customer.upsert({
|
||||
where: { id: 1 },
|
||||
update: {},
|
||||
create: {
|
||||
name: 'أحمد محمد',
|
||||
phone: '+966501234567',
|
||||
email: 'ahmed@example.com',
|
||||
address: 'الرياض، المملكة العربية السعودية',
|
||||
},
|
||||
});
|
||||
|
||||
const sampleVehicle = await prisma.vehicle.upsert({
|
||||
where: { id: 1 },
|
||||
update: {},
|
||||
create: {
|
||||
plateNumber: 'ABC-1234',
|
||||
bodyType: 'سيدان',
|
||||
manufacturer: 'تويوتا',
|
||||
model: 'كامري',
|
||||
trim: 'GLE',
|
||||
year: 2022,
|
||||
transmission: 'Automatic',
|
||||
fuel: 'Gasoline',
|
||||
cylinders: 4,
|
||||
engineDisplacement: 2.5,
|
||||
useType: 'personal',
|
||||
ownerId: sampleCustomer.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Get maintenance types for sample visits
|
||||
const periodicMaintenanceType = await prisma.maintenanceType.findFirst({
|
||||
where: { name: 'صيانة دورية' }
|
||||
});
|
||||
const brakeRepairType = await prisma.maintenanceType.findFirst({
|
||||
where: { name: 'إصلاح الفرامل' }
|
||||
});
|
||||
|
||||
// Create some sample maintenance visits
|
||||
const sampleVisit1 = await prisma.maintenanceVisit.upsert({
|
||||
where: { id: 1 },
|
||||
update: {},
|
||||
create: {
|
||||
vehicleId: sampleVehicle.id,
|
||||
customerId: sampleCustomer.id,
|
||||
maintenanceTypeId: periodicMaintenanceType!.id,
|
||||
description: 'تغيير زيت المحرك وفلتر الزيت وفحص شامل للمركبة',
|
||||
cost: 250.00,
|
||||
paymentStatus: 'paid',
|
||||
kilometers: 45000,
|
||||
visitDate: new Date('2024-01-15'),
|
||||
nextVisitDelay: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const sampleVisit2 = await prisma.maintenanceVisit.upsert({
|
||||
where: { id: 2 },
|
||||
update: {},
|
||||
create: {
|
||||
vehicleId: sampleVehicle.id,
|
||||
customerId: sampleCustomer.id,
|
||||
maintenanceTypeId: brakeRepairType!.id,
|
||||
description: 'تغيير أقراص الفرامل الأمامية وتيل الفرامل',
|
||||
cost: 450.00,
|
||||
paymentStatus: 'paid',
|
||||
kilometers: 47500,
|
||||
visitDate: new Date('2024-03-20'),
|
||||
nextVisitDelay: 12,
|
||||
},
|
||||
});
|
||||
|
||||
const sampleVisit3 = await prisma.maintenanceVisit.upsert({
|
||||
where: { id: 3 },
|
||||
update: {},
|
||||
create: {
|
||||
vehicleId: sampleVehicle.id,
|
||||
customerId: sampleCustomer.id,
|
||||
maintenanceTypeId: periodicMaintenanceType!.id,
|
||||
description: 'تغيير زيت المحرك وفلتر الهواء وفحص البطارية',
|
||||
cost: 180.00,
|
||||
paymentStatus: 'pending',
|
||||
kilometers: 52000,
|
||||
visitDate: new Date('2024-07-15'),
|
||||
nextVisitDelay: 6,
|
||||
},
|
||||
});
|
||||
|
||||
// Create income records for the maintenance visits
|
||||
await prisma.income.upsert({
|
||||
where: { id: 1 },
|
||||
update: {},
|
||||
create: {
|
||||
maintenanceVisitId: sampleVisit1.id,
|
||||
amount: sampleVisit1.cost,
|
||||
incomeDate: sampleVisit1.visitDate,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.income.upsert({
|
||||
where: { id: 2 },
|
||||
update: {},
|
||||
create: {
|
||||
maintenanceVisitId: sampleVisit2.id,
|
||||
amount: sampleVisit2.cost,
|
||||
incomeDate: sampleVisit2.visitDate,
|
||||
},
|
||||
});
|
||||
|
||||
// Update the vehicle with last visit information
|
||||
await prisma.vehicle.update({
|
||||
where: { id: sampleVehicle.id },
|
||||
data: {
|
||||
lastVisitDate: sampleVisit3.visitDate,
|
||||
suggestedNextVisitDate: new Date('2025-01-15'), // 6 months after last visit
|
||||
},
|
||||
});
|
||||
|
||||
console.log('✅ Created sample customer, vehicle, and maintenance visits');
|
||||
|
||||
console.log('🎉 Database seeded successfully!');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error('❌ Error seeding database:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
54
prisma/settingsSeed.ts
Normal file
54
prisma/settingsSeed.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function seedSettings() {
|
||||
console.log('🌱 Seeding settings...');
|
||||
|
||||
const defaultSettings = [
|
||||
{
|
||||
key: 'dateFormat',
|
||||
value: 'ar-SA',
|
||||
description: 'Date format locale (ar-SA or en-US)'
|
||||
},
|
||||
{
|
||||
key: 'currency',
|
||||
value: 'JOD',
|
||||
description: 'Currency code (JOD, USD, EUR, etc.)'
|
||||
},
|
||||
{
|
||||
key: 'numberFormat',
|
||||
value: 'ar-SA',
|
||||
description: 'Number format locale (ar-SA or en-US)'
|
||||
},
|
||||
{
|
||||
key: 'currencySymbol',
|
||||
value: 'د.أ',
|
||||
description: 'Currency symbol display'
|
||||
},
|
||||
{
|
||||
key: 'dateDisplayFormat',
|
||||
value: 'dd/MM/yyyy',
|
||||
description: 'Date display format pattern'
|
||||
}
|
||||
];
|
||||
|
||||
for (const setting of defaultSettings) {
|
||||
await prisma.settings.upsert({
|
||||
where: { key: setting.key },
|
||||
update: {},
|
||||
create: setting
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ Settings seeded successfully');
|
||||
}
|
||||
|
||||
seedSettings()
|
||||
.catch((e) => {
|
||||
console.error('❌ Error seeding settings:', e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user