diff --git a/app/Console/Commands/ResetApp.php b/app/Console/Commands/ResetApp.php index d4e3fc42..6724286f 100644 --- a/app/Console/Commands/ResetApp.php +++ b/app/Console/Commands/ResetApp.php @@ -6,6 +6,8 @@ use Illuminate\Console\Command; use Illuminate\Console\ConfirmableTrait; use Illuminate\Support\Facades\Artisan; +use function Laravel\Prompts\confirm; + class ResetApp extends Command { use ConfirmableTrait; @@ -39,30 +41,49 @@ class ResetApp extends Command * * @return mixed */ + /** + * Execute the console command to reset the application. + * + * This will: + * 1. Enable maintenance mode to prevent access during reset + * 2. Fresh migrate the database with initial seeds + * 3. Seed demo data using DemoSeeder + * 4. Clear all application caches + * 5. Disable maintenance mode + * + * The --force flag can be used to skip confirmation prompt. + */ public function handle(): void { - if (! $this->confirmToProceed()) { - return; + + if (! $this->option('force')) { + if (! confirm('Are you sure you want to reset the application?')) { + $this->components->error('Reset cancelled'); + + return; + } } - $this->info('Running migrate:fresh'); + // Enable maintenance mode to prevent access during reset + $this->info('Activating maintenance mode...'); + Artisan::call('down'); + // Fresh migrate database and run initial seeds + $this->info('Running migrate:fresh'); Artisan::call('migrate:fresh --seed --force'); + // Seed demo data $this->info('Seeding database'); - Artisan::call('db:seed', ['--class' => 'DemoSeeder', '--force' => true]); - $path = base_path('.env'); + // Clear all application caches + $this->info('Clearing cache...'); + Artisan::call('optimize:clear'); - if (file_exists($path)) { - file_put_contents($path, str_replace( - 'APP_DEBUG=true', - 'APP_DEBUG=false', - file_get_contents($path) - )); - } + // Disable maintenance mode + $this->info('Deactivating maintenance mode...'); + Artisan::call('up'); - $this->info('App has been reset successfully'); + $this->info('App reset completed successfully!'); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f140cb13..d5327c30 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -60,6 +60,12 @@ class AppServiceProvider extends ServiceProvider $this->bootAuth(); $this->bootBroadcast(); + + // In demo mode, prevent all outgoing emails and notifications + if (config('app.env') === 'demo') { + \Illuminate\Support\Facades\Mail::fake(); + \Illuminate\Support\Facades\Notification::fake(); + } } /** diff --git a/bootstrap/app.php b/bootstrap/app.php index b71fd536..be24cefa 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -67,6 +67,7 @@ return Application::configure(basePath: dirname(__DIR__)) \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class, ]); + }) ->withExceptions(function (Exceptions $exceptions) { // diff --git a/database/seeders/DemoSeeder.php b/database/seeders/DemoSeeder.php index dc30403a..49ca0217 100644 --- a/database/seeders/DemoSeeder.php +++ b/database/seeders/DemoSeeder.php @@ -2,11 +2,15 @@ namespace Database\Seeders; -use App\Models\Address; +use App\Models\Company; +use App\Models\CompanySetting; +use App\Models\Customer; use App\Models\Setting; use App\Models\User; use App\Space\InstallUtils; use Illuminate\Database\Seeder; +use Silber\Bouncer\BouncerFacade; +use Vinkla\Hashids\Facades\Hashids; class DemoSeeder extends Seeder { @@ -15,14 +19,60 @@ class DemoSeeder extends Seeder */ public function run(): void { - $user = User::whereIs('super admin')->first(); + // Create demo user + $user = User::factory()->create([ + 'email' => 'demo@invoiceshelf.com', + 'name' => 'Demo User', + 'role' => 'super admin', + 'password' => 'demo', + ]); - $user->setSettings(['language' => 'en']); + // Create demo company + $company = Company::factory()->create([ + 'name' => 'Demo Company', + 'owner_id' => $user->id, + 'slug' => 'demo-company', + ]); - Address::create(['company_id' => $user->companies()->first()->id, 'country_id' => 1]); + $company->unique_hash = Hashids::connection(Company::class)->encode($company->id); + $company->save(); + $company->setupDefaultData(); + $user->companies()->attach($company->id); + BouncerFacade::scope()->to($company->id); + $user->assign('super admin'); + + // Set default user settings + $user->setSettings([ + 'language' => 'en', + 'timezone' => 'UTC', + 'date_format' => 'DD-MM-YYYY', + 'currency_id' => 1, // USD + ]); + + // Set company settings + CompanySetting::setSettings([ + 'currency' => 1, + 'date_format' => 'DD-MM-YYYY', + 'language' => 'en', + 'timezone' => 'UTC', + 'fiscal_year' => 'calendar_year', + 'tax_per_item' => false, + 'discount_per_item' => false, + 'invoice_prefix' => 'INV-', + 'estimate_prefix' => 'EST-', + 'payment_prefix' => 'PAY-', + ], $company->id); + + // Create demo customers + Customer::factory()->count(5)->create([ + 'company_id' => $company->id, + ]); + + // Mark profile setup as complete Setting::setSetting('profile_complete', 'COMPLETED'); + // Create installation marker InstallUtils::createDbMarker(); } } diff --git a/resources/scripts/admin/views/auth/Login.vue b/resources/scripts/admin/views/auth/Login.vue index 4b9429e6..08cacf40 100644 --- a/resources/scripts/admin/views/auth/Login.vue +++ b/resources/scripts/admin/views/auth/Login.vue @@ -57,7 +57,7 @@ diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php index eb49297c..aaee5c79 100644 --- a/resources/views/app.blade.php +++ b/resources/views/app.blade.php @@ -64,6 +64,10 @@ @endif + @if(config('app.env') === 'demo') + window.demo_mode = true + @endif + window.InvoiceShelf.start() diff --git a/routes/console.php b/routes/console.php index b7f7af44..1acb6745 100644 --- a/routes/console.php +++ b/routes/console.php @@ -3,13 +3,15 @@ use App\Models\CompanySetting; use App\Models\RecurringInvoice; use App\Space\InstallUtils; -use Illuminate\Foundation\Inspiring; -use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Schedule; -Artisan::command('inspire', function () { - $this->comment(Inspiring::quote()); -})->purpose('Display an inspiring quote')->hourly(); +// Only run in demo environment +if (config('app.env') === 'demo') { + Schedule::command('reset:app --force') + ->daily() + ->runInBackground() + ->withoutOverlapping(); +} if (InstallUtils::isDbCreated()) { Schedule::command('check:invoices:status')