diff --git a/backend/database/.gitignore b/backend/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/backend/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/backend/database/factories/ClientFactory.php b/backend/database/factories/ClientFactory.php new file mode 100644 index 0000000..fb54537 --- /dev/null +++ b/backend/database/factories/ClientFactory.php @@ -0,0 +1,20 @@ + $this->faker->unique()->numerify('K###'), + 'name' => $this->faker->company(), + 'notes' => null, + ]; + } +} diff --git a/backend/database/factories/HistoryEntryFactory.php b/backend/database/factories/HistoryEntryFactory.php new file mode 100644 index 0000000..a951149 --- /dev/null +++ b/backend/database/factories/HistoryEntryFactory.php @@ -0,0 +1,25 @@ + Client::factory(), + 'type' => $this->faker->randomElement(HistoryEntryType::cases())->value, + 'body' => $this->faker->paragraph(), + 'author_id' => User::factory(), + 'updated_by' => null, + ]; + } +} diff --git a/backend/database/factories/UserFactory.php b/backend/database/factories/UserFactory.php new file mode 100644 index 0000000..584104c --- /dev/null +++ b/backend/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/backend/database/migrations/2014_10_12_000000_create_users_table.php b/backend/database/migrations/2014_10_12_000000_create_users_table.php new file mode 100644 index 0000000..444fafb --- /dev/null +++ b/backend/database/migrations/2014_10_12_000000_create_users_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/backend/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/backend/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php new file mode 100644 index 0000000..81a7229 --- /dev/null +++ b/backend/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -0,0 +1,28 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/backend/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/backend/database/migrations/2019_08_19_000000_create_failed_jobs_table.php new file mode 100644 index 0000000..249da81 --- /dev/null +++ b/backend/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/backend/database/migrations/2026_06_23_095329_add_role_to_users_table.php b/backend/database/migrations/2026_06_23_095329_add_role_to_users_table.php new file mode 100644 index 0000000..885b491 --- /dev/null +++ b/backend/database/migrations/2026_06_23_095329_add_role_to_users_table.php @@ -0,0 +1,28 @@ +enum('role', ['admin', 'staff'])->default('staff')->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('role'); + }); + } +}; diff --git a/backend/database/migrations/2026_06_23_095329_create_clients_table.php b/backend/database/migrations/2026_06_23_095329_create_clients_table.php new file mode 100644 index 0000000..6475674 --- /dev/null +++ b/backend/database/migrations/2026_06_23_095329_create_clients_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('client_number')->unique(); + $table->string('name'); + $table->json('notes')->nullable(); + $table->timestamps(); + + $table->fullText(['client_number', 'name']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('clients'); + } +}; diff --git a/backend/database/migrations/2026_06_23_095330_create_history_entries_table.php b/backend/database/migrations/2026_06_23_095330_create_history_entries_table.php new file mode 100644 index 0000000..545f09d --- /dev/null +++ b/backend/database/migrations/2026_06_23_095330_create_history_entries_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('client_id')->constrained()->cascadeOnDelete(); + $table->enum('type', [ + 'phone_call', 'consultation', 'recommendation', + 'instruction', 'remark', 'other', + ]); + $table->text('body'); + $table->foreignId('author_id')->constrained('users'); + $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete(); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('history_entries'); + } +}; diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..ee1a533 --- /dev/null +++ b/backend/database/seeders/DatabaseSeeder.php @@ -0,0 +1,30 @@ +create([ + 'name' => 'Admin', + 'email' => 'admin@example.com', + 'password' => bcrypt('password'), + 'role' => 'admin', + ]); + + $clients = Client::factory(10)->create(); + + $clients->each(function (Client $client) use ($admin) { + HistoryEntry::factory(3)->create([ + 'client_id' => $client->id, + 'author_id' => $admin->id, + ]); + }); + } +}