6 Commits

Author SHA1 Message Date
89b3ddb793 Merge pull request 'Fix persistent LDAP volumes preventing bootstrap.ldif from reloading' (#11) from fix/ldap-bootstrap into master
Some checks failed
linter / quality (push) Successful in 1m22s
security / Static Analysis (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
security / Dependency Audit (push) Has been cancelled
Reviewed-on: #11
2026-03-06 12:08:18 +08:00
a4a31c97e3 Merge pull request 'Update project config and remove tailwindcss skill' (#10) from feature/ldap-email-bootstrap into master
Some checks failed
linter / quality (push) Has been cancelled
security / Dependency Audit (push) Has been cancelled
security / Static Analysis (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Reviewed-on: #10
2026-03-06 12:02:20 +08:00
d59ec55999 Remove persistent LDAP volumes so bootstrap.ldif always applies on startup
All checks were successful
linter / quality (pull_request) Successful in 1m45s
security / Dependency Audit (pull_request) Successful in 1m21s
security / Static Analysis (pull_request) Successful in 2m11s
tests / ci (8.4) (pull_request) Successful in 1m27s
tests / ci (8.5) (pull_request) Successful in 2m21s
The osixia/openldap image only runs bootstrap LDIF when the database is
empty. Named volumes (sail-ldap-data, sail-ldap-config) caused changes
to bootstrap.ldif to be ignored after the first run. Removing these
volumes ensures the test LDAP directory is always seeded fresh from the
bootstrap file on each sail up.
2026-03-06 04:02:18 +00:00
2ef5014919 Merge pull request 'Default Bootstrap theme to dark mode' (#9) from feature/dark-mode-default into master
Some checks failed
linter / quality (push) Has been cancelled
security / Dependency Audit (push) Has been cancelled
security / Static Analysis (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Reviewed-on: #9
2026-03-06 12:01:57 +08:00
e07984fa97 Default Bootstrap theme to dark mode
All checks were successful
linter / quality (pull_request) Successful in 1m51s
security / Dependency Audit (pull_request) Successful in 1m24s
security / Static Analysis (pull_request) Successful in 1m53s
tests / ci (8.4) (pull_request) Successful in 1m28s
tests / ci (8.5) (pull_request) Successful in 1m54s
- Change default theme from light to dark for first-time visitors
- Fix Alpine reactive theme toggle so icons and data-bs-theme update correctly
- Remove hardcoded bg-light classes that prevented dark mode from applying
- Fix broken duplicate Bootstrap bundle import in app.js
2026-03-06 03:54:34 +00:00
dd3c623bfc Merge pull request 'Seed OpenLDAP with bootstrap users including email addresses' (#8) from feature/ldap-email-bootstrap into master
All checks were successful
linter / quality (push) Successful in 1m34s
security / Dependency Audit (push) Successful in 1m24s
security / Static Analysis (push) Successful in 2m42s
tests / ci (8.4) (push) Successful in 1m23s
tests / ci (8.5) (push) Successful in 1m51s
Reviewed-on: #8
2026-03-06 10:48:31 +08:00
6 changed files with 4 additions and 145 deletions

View File

@@ -17,7 +17,7 @@ class ApprovalService
$workflow = $travelRequest->workflow;
if (! $workflow) {
throw new \RuntimeException('No active approval workflow is configured.');
return;
}
$steps = $workflow->steps()->orderBy('order')->get();

View File

@@ -97,8 +97,6 @@ services:
LDAP_READONLY_USER_USERNAME: '${LDAP_READONLY_USERNAME:-readonly}'
LDAP_READONLY_USER_PASSWORD: '${LDAP_READONLY_PASSWORD:-readonly}'
volumes:
- 'sail-ldap-data:/var/lib/ldap'
- 'sail-ldap-config:/etc/ldap/slapd.d'
- './docker/openldap/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/bootstrap.ldif'
networks:
- sail
@@ -136,7 +134,3 @@ volumes:
driver: local
sail-redis:
driver: local
sail-ldap-data:
driver: local
sail-ldap-config:
driver: local

View File

@@ -1,6 +1,6 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}"
x-data="{ theme: localStorage.getItem('theme') || 'light' }"
x-data="{ theme: localStorage.getItem('theme') || 'dark' }"
x-init="$watch('theme', val => { document.documentElement.setAttribute('data-bs-theme', val); localStorage.setItem('theme', val); }); document.documentElement.setAttribute('data-bs-theme', theme);"
:data-bs-theme="theme"
>

View File

@@ -1,6 +1,6 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}"
x-data="{ theme: localStorage.getItem('theme') || 'light' }"
x-data="{ theme: localStorage.getItem('theme') || 'dark' }"
x-init="document.documentElement.setAttribute('data-bs-theme', theme);"
:data-bs-theme="theme"
>

View File

@@ -92,6 +92,7 @@ new #[Layout('components.layouts.app')] class extends Component {
public function saveDraft(): void
{
$this->saveRequest(submit: false);
session()->flash('success', 'Draft saved successfully.');
}
public function submit(): void
@@ -101,12 +102,6 @@ new #[Layout('components.layouts.app')] class extends Component {
private function saveRequest(bool $submit): void
{
if ($submit && ! ApprovalWorkflow::where('is_active', true)->exists()) {
$this->addError('workflow', 'No active approval workflow is configured. Please contact an administrator.');
return;
}
$this->validate([
'emergencyFullName' => ['required', 'string', 'max:255'],
'emergencyPhone' => ['required', 'string', 'max:50'],
@@ -187,8 +182,6 @@ new #[Layout('components.layouts.app')] class extends Component {
if ($submit) {
app(ApprovalService::class)->submit($travelRequest);
session()->flash('success', 'Travel request submitted for approval.');
} else {
session()->flash('success', 'Draft saved successfully.');
}
$this->redirect(route('travel-requests.show', $travelRequest), navigate: true);
@@ -216,10 +209,6 @@ new #[Layout('components.layouts.app')] class extends Component {
</div>
@endif
@error('workflow')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
<form wire:submit.prevent>
{{-- Applicant Details --}}

View File

@@ -1,124 +0,0 @@
<?php
namespace Tests\Feature;
use App\Enums\ApprovalStatus;
use App\Enums\JourneyMethod;
use App\Enums\TravelStatus;
use App\Models\ApprovalStep;
use App\Models\ApprovalWorkflow;
use App\Models\TravelRequest;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class TravelRequestSubmissionTest extends TestCase
{
use RefreshDatabase;
private User $staff;
/** @var array<string, mixed> */
private array $validFormData;
protected function setUp(): void
{
parent::setUp();
Role::firstOrCreate(['name' => 'staff']);
Role::firstOrCreate(['name' => 'travel_approver']);
Role::firstOrCreate(['name' => 'administrator']);
$this->staff = User::factory()->create();
$this->staff->assignRole('staff');
$this->validFormData = [
'emergencyFullName' => 'Jane Doe',
'emergencyPhone' => '0400000000',
'emergencyRelationship' => 'Spouse',
'reasonSummary' => 'Attending a medical conference in Sydney',
'journeys' => [
[
'origin' => 'Perth',
'destination' => 'Sydney',
'date' => '2026-04-01',
'time' => '09:00',
'method' => JourneyMethod::Air->value,
],
],
'costCodes' => [],
];
}
private function makeWorkflow(): ApprovalWorkflow
{
$workflow = ApprovalWorkflow::factory()->create(['is_active' => true]);
ApprovalStep::factory()->create([
'workflow_id' => $workflow->id,
'order' => 1,
'name' => 'Travel Approver Review',
'role' => 'travel_approver',
]);
return $workflow;
}
public function test_submitting_form_with_active_workflow_sets_status_to_pending(): void
{
$this->makeWorkflow();
Livewire::actingAs($this->staff)
->test('travel-request.create')
->set($this->validFormData)
->call('submit');
$request = TravelRequest::first();
$this->assertNotNull($request);
$this->assertSame(TravelStatus::Pending, $request->status);
$this->assertNotNull($request->submitted_at);
}
public function test_submitting_form_with_active_workflow_creates_pending_approval(): void
{
$this->makeWorkflow();
Livewire::actingAs($this->staff)
->test('travel-request.create')
->set($this->validFormData)
->call('submit');
$request = TravelRequest::first();
$this->assertCount(1, $request->approvals);
$this->assertSame(ApprovalStatus::Pending->value, $request->approvals->first()->status->value);
}
public function test_submitting_form_with_no_active_workflow_shows_error(): void
{
Livewire::actingAs($this->staff)
->test('travel-request.create')
->set($this->validFormData)
->call('submit')
->assertHasErrors(['workflow']);
$this->assertDatabaseCount('travel_requests', 0);
}
public function test_saving_draft_does_not_submit_for_approval(): void
{
$this->makeWorkflow();
Livewire::actingAs($this->staff)
->test('travel-request.create')
->set($this->validFormData)
->call('saveDraft');
$request = TravelRequest::first();
$this->assertNotNull($request);
$this->assertSame(TravelStatus::Draft, $request->status);
$this->assertNull($request->submitted_at);
$this->assertCount(0, $request->approvals);
}
}