Build
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('provider_profiles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete();
|
||||
$table->string('business_name');
|
||||
$table->string('headline')->nullable();
|
||||
$table->text('bio')->nullable();
|
||||
$table->string('service_area')->nullable();
|
||||
$table->text('skills')->nullable();
|
||||
$table->text('tools')->nullable();
|
||||
$table->text('certifications')->nullable();
|
||||
$table->string('insurance_status')->nullable();
|
||||
$table->text('rate_card')->nullable();
|
||||
$table->text('travel_policy')->nullable();
|
||||
$table->text('availability_notes')->nullable();
|
||||
$table->string('website_url')->nullable();
|
||||
$table->string('phone')->nullable();
|
||||
$table->boolean('public_contact')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('provider_profiles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('buyer_profiles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete();
|
||||
$table->string('company_name');
|
||||
$table->string('headline')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->text('service_categories')->nullable();
|
||||
$table->text('hiring_regions')->nullable();
|
||||
$table->text('vendor_onboarding')->nullable();
|
||||
$table->text('payment_terms')->nullable();
|
||||
$table->string('website_url')->nullable();
|
||||
$table->string('contact_email')->nullable();
|
||||
$table->boolean('public_contact')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('buyer_profiles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('social_posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('title')->nullable();
|
||||
$table->text('body');
|
||||
$table->string('visibility')->default('public');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('social_posts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('job_posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('buyer_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('title');
|
||||
$table->string('status')->default('open');
|
||||
$table->string('service_category')->nullable();
|
||||
$table->string('location');
|
||||
$table->timestamp('starts_at')->nullable();
|
||||
$table->string('time_window')->nullable();
|
||||
$table->text('scope');
|
||||
$table->text('required_skills')->nullable();
|
||||
$table->text('required_tools')->nullable();
|
||||
$table->text('deliverables')->nullable();
|
||||
$table->text('payment_terms')->nullable();
|
||||
$table->text('vendor_onboarding')->nullable();
|
||||
$table->string('visibility')->default('public');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('job_posts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('quotes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('job_post_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('provider_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('status')->default('submitted');
|
||||
$table->decimal('requested_amount', 10, 2)->nullable();
|
||||
$table->string('rate_summary')->nullable();
|
||||
$table->text('message')->nullable();
|
||||
$table->text('terms')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('quotes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('work_orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('job_post_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('buyer_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('provider_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('accepted_quote_id')->nullable()->constrained('quotes')->nullOnDelete();
|
||||
$table->string('status')->default('assigned');
|
||||
$table->json('status_history')->nullable();
|
||||
$table->text('agreed_terms')->nullable();
|
||||
$table->text('deliverables_checklist')->nullable();
|
||||
$table->text('completion_notes')->nullable();
|
||||
$table->timestamp('en_route_at')->nullable();
|
||||
$table->timestamp('on_site_at')->nullable();
|
||||
$table->timestamp('started_at')->nullable();
|
||||
$table->timestamp('completed_at')->nullable();
|
||||
$table->timestamp('approved_at')->nullable();
|
||||
$table->timestamp('closed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('work_orders');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('reviews', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('work_order_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('reviewer_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('reviewee_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->unsignedTinyInteger('rating');
|
||||
$table->text('body')->nullable();
|
||||
$table->string('review_type');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('reviews');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('disputes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('work_order_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('opened_by_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('status')->default('open');
|
||||
$table->string('summary');
|
||||
$table->text('claim');
|
||||
$table->text('response')->nullable();
|
||||
$table->text('evidence_notes')->nullable();
|
||||
$table->text('recommended_resolution')->nullable();
|
||||
$table->json('peer_votes')->nullable();
|
||||
$table->timestamp('resolved_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('disputes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('external_profile_imports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('provider_profile_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('platform');
|
||||
$table->string('external_id')->nullable();
|
||||
$table->string('profile_url')->nullable();
|
||||
$table->string('status')->default('manual');
|
||||
$table->decimal('rating', 3, 2)->nullable();
|
||||
$table->unsignedInteger('review_count')->nullable();
|
||||
$table->unsignedInteger('completed_jobs')->nullable();
|
||||
$table->json('metrics')->nullable();
|
||||
$table->json('review_snapshots')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamp('imported_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('external_profile_imports');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('notification_preferences', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete();
|
||||
$table->boolean('job_alerts')->default(true);
|
||||
$table->boolean('quote_updates')->default(true);
|
||||
$table->boolean('work_order_updates')->default(true);
|
||||
$table->boolean('review_updates')->default(true);
|
||||
$table->boolean('dispute_updates')->default(true);
|
||||
$table->boolean('email_enabled')->default(true);
|
||||
$table->boolean('push_enabled')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('notification_preferences');
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\JobPost;
|
||||
use App\Models\WorkOrder;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
@@ -46,5 +48,110 @@ class DatabaseSeeder extends Seeder
|
||||
['name' => 'Hybrid User', 'password' => Hash::make('password')]
|
||||
);
|
||||
$hybrid->syncRoles(['provider', 'buyer']);
|
||||
|
||||
foreach ([$admin, $provider, $buyer, $hybrid] as $user) {
|
||||
$user->notificationPreference()->firstOrCreate([]);
|
||||
}
|
||||
|
||||
$providerProfile = $provider->providerProfile()->updateOrCreate(
|
||||
['user_id' => $provider->id],
|
||||
[
|
||||
'business_name' => 'Independent Field Tech Demo',
|
||||
'headline' => 'POS, networking, cabling, and onsite IT support',
|
||||
'bio' => 'Demo provider profile for the technician-owned exchange.',
|
||||
'service_area' => 'Central US with regional travel',
|
||||
'skills' => "POS\nNetworking\nStructured cabling\nPrinter support",
|
||||
'tools' => "Laptop\nToner/probe\nCable tester\nBasic hand tools",
|
||||
'certifications' => 'Low-voltage and platform-specific certifications can be listed here.',
|
||||
'insurance_status' => 'Provider supplied',
|
||||
'rate_card' => "Two-hour minimum\nStandard onsite hourly rate set by provider\nTravel negotiated per job",
|
||||
'travel_policy' => 'Travel terms are negotiated directly with buyers.',
|
||||
'availability_notes' => 'Weekdays and scheduled after-hours work.',
|
||||
]
|
||||
);
|
||||
|
||||
$providerProfile->externalImports()->updateOrCreate(
|
||||
['platform' => 'Field Nation', 'external_id' => '172-630'],
|
||||
[
|
||||
'status' => 'manual',
|
||||
'notes' => 'Placeholder for imported Field Nation profile/rating history. Public search did not expose a profile by ID.',
|
||||
'imported_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
$buyer->buyerProfile()->updateOrCreate(
|
||||
['user_id' => $buyer->id],
|
||||
[
|
||||
'company_name' => 'Demo Buyer Services',
|
||||
'headline' => 'Transparent field service buyer profile',
|
||||
'description' => 'Demo buyer profile showing onboarding and payment terms.',
|
||||
'service_categories' => "Networking\nPOS\nPrinter support",
|
||||
'hiring_regions' => 'Nationwide',
|
||||
'vendor_onboarding' => 'Direct vendor onboarding outside the platform.',
|
||||
'payment_terms' => 'Direct payment between buyer and provider.',
|
||||
]
|
||||
);
|
||||
|
||||
$provider->socialPosts()->firstOrCreate(
|
||||
['title' => 'Available for regional dispatch'],
|
||||
['body' => 'Demo provider post showing the community feed.', 'visibility' => 'public']
|
||||
);
|
||||
|
||||
$job = JobPost::firstOrCreate(
|
||||
['title' => 'Demo POS troubleshooting visit'],
|
||||
[
|
||||
'buyer_id' => $buyer->id,
|
||||
'status' => 'assigned',
|
||||
'service_category' => 'POS',
|
||||
'location' => 'Tulsa, OK',
|
||||
'time_window' => 'Morning',
|
||||
'scope' => 'Troubleshoot intermittent POS terminal connectivity.',
|
||||
'required_skills' => 'POS and basic networking',
|
||||
'required_tools' => 'Laptop, patch cables, toner/probe',
|
||||
'deliverables' => "Check in\nPhotos\nResolution notes",
|
||||
'payment_terms' => 'Direct buyer/provider payment terms.',
|
||||
'vendor_onboarding' => 'Buyer handles direct onboarding.',
|
||||
'visibility' => 'public',
|
||||
]
|
||||
);
|
||||
|
||||
$quote = $job->quotes()->firstOrCreate(
|
||||
['provider_id' => $provider->id],
|
||||
[
|
||||
'status' => 'accepted',
|
||||
'requested_amount' => 250,
|
||||
'rate_summary' => 'Two-hour minimum plus travel if needed',
|
||||
'message' => 'Can handle this visit with standard POS/network tools.',
|
||||
'terms' => 'Direct invoice to buyer after completion.',
|
||||
]
|
||||
);
|
||||
|
||||
$workOrder = WorkOrder::firstOrCreate(
|
||||
['job_post_id' => $job->id],
|
||||
[
|
||||
'buyer_id' => $buyer->id,
|
||||
'provider_id' => $provider->id,
|
||||
'accepted_quote_id' => $quote->id,
|
||||
'status' => 'assigned',
|
||||
'agreed_terms' => $quote->terms,
|
||||
'deliverables_checklist' => $job->deliverables,
|
||||
'status_history' => [['status' => 'assigned', 'user_id' => $buyer->id, 'at' => now()->toIso8601String()]],
|
||||
]
|
||||
);
|
||||
|
||||
$workOrder->reviews()->firstOrCreate(
|
||||
['reviewer_id' => $buyer->id, 'reviewee_id' => $provider->id],
|
||||
['rating' => 5, 'body' => 'Demo review record.', 'review_type' => 'buyer_to_provider']
|
||||
);
|
||||
|
||||
$workOrder->disputes()->firstOrCreate(
|
||||
['summary' => 'Demo peer review record'],
|
||||
[
|
||||
'opened_by_id' => $provider->id,
|
||||
'status' => 'open',
|
||||
'claim' => 'This is a demo dispute record for peer review workflow testing.',
|
||||
'evidence_notes' => 'Screenshots, chat, and deliverable notes would be attached here later.',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user