Skip to content

Commit e00181a

Browse files
committed
Incremental architecture revision
1 parent cd0ddd8 commit e00181a

15 files changed

+3228
-47
lines changed

bootstrap/app.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66

77
return Application::configure(basePath: dirname(__DIR__))
88
->withRouting(
9-
web: __DIR__.'/../routes/web.php',
10-
commands: __DIR__.'/../routes/console.php',
9+
web: __DIR__ . '/../routes/web.php',
10+
commands: __DIR__ . '/../routes/console.php',
1111
health: '/up',
1212
)
1313
->withMiddleware(function (Middleware $middleware) {
1414
$middleware->web(append: [
15+
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
1516
\App\Http\Middleware\HandleInertiaRequests::class,
1617
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
1718
]);
@@ -20,4 +21,5 @@
2021
})
2122
->withExceptions(function (Exceptions $exceptions) {
2223
//
23-
})->create();
24+
})
25+
->create();

config/sanctum.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
use Laravel\Sanctum\Sanctum;
4+
5+
return [
6+
7+
/*
8+
|--------------------------------------------------------------------------
9+
| Stateful Domains
10+
|--------------------------------------------------------------------------
11+
|
12+
| Requests from the following domains / hosts will receive stateful API
13+
| authentication cookies. Typically, these should include your local
14+
| and production domains which access your API via a frontend SPA.
15+
|
16+
*/
17+
18+
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
19+
'%s%s',
20+
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
21+
Sanctum::currentApplicationUrlWithPort()
22+
))),
23+
24+
/*
25+
|--------------------------------------------------------------------------
26+
| Sanctum Guards
27+
|--------------------------------------------------------------------------
28+
|
29+
| This array contains the authentication guards that will be checked when
30+
| Sanctum is trying to authenticate a request. If none of these guards
31+
| are able to authenticate the request, Sanctum will use the bearer
32+
| token that's present on an incoming request for authentication.
33+
|
34+
*/
35+
36+
'guard' => ['web'],
37+
38+
/*
39+
|--------------------------------------------------------------------------
40+
| Expiration Minutes
41+
|--------------------------------------------------------------------------
42+
|
43+
| This value controls the number of minutes until an issued token will be
44+
| considered expired. This will override any values set in the token's
45+
| "expires_at" attribute, but first-party sessions are not affected.
46+
|
47+
*/
48+
49+
'expiration' => null,
50+
51+
/*
52+
|--------------------------------------------------------------------------
53+
| Token Prefix
54+
|--------------------------------------------------------------------------
55+
|
56+
| Sanctum can prefix new tokens in order to take advantage of numerous
57+
| security scanning initiatives maintained by open source platforms
58+
| that notify developers if they commit tokens into repositories.
59+
|
60+
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
61+
|
62+
*/
63+
64+
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
65+
66+
/*
67+
|--------------------------------------------------------------------------
68+
| Sanctum Middleware
69+
|--------------------------------------------------------------------------
70+
|
71+
| When authenticating your first-party SPA with Sanctum you may need to
72+
| customize some of the middleware Sanctum uses while processing the
73+
| request. You may change the middleware listed below as required.
74+
|
75+
*/
76+
77+
'middleware' => [
78+
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
79+
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
80+
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
81+
],
82+
83+
];

database/migrations/0001_01_01_000000_create_users_table.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
/**
1010
* Run the migrations.
1111
*/
12-
public function up(): void
13-
{
12+
public function up(): void
13+
{
14+
if (!Schema::hasTable('prod.users')) {
1415
Schema::create('prod.users', function (Blueprint $table) {
1516
$table->id();
1617
$table->string('name');
@@ -19,22 +20,27 @@ public function up(): void
1920
$table->string('password');
2021
$table->rememberToken();
2122
$table->timestamps();
22-
});
23+
});
24+
}
2325

26+
if (!Schema::hasTable('prod.password_reset_tokens')) {
2427
Schema::create('prod.password_reset_tokens', function (Blueprint $table) {
2528
$table->string('email')->primary();
2629
$table->string('token');
2730
$table->timestamp('created_at')->nullable();
28-
});
31+
});
32+
}
2933

34+
if (!Schema::hasTable('prod.sessions')) {
3035
Schema::create('prod.sessions', function (Blueprint $table) {
3136
$table->string('id')->primary();
3237
$table->foreignId('user_id')->nullable()->index();
3338
$table->string('ip_address', 45)->nullable();
3439
$table->text('user_agent')->nullable();
3540
$table->longText('payload');
3641
$table->integer('last_activity')->index();
37-
});
42+
});
43+
}
3844
}
3945

4046
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('personal_access_tokens', function (Blueprint $table) {
15+
$table->id();
16+
$table->morphs('tokenable');
17+
$table->string('name');
18+
$table->string('token', 64)->unique();
19+
$table->text('abilities')->nullable();
20+
$table->timestamp('last_used_at')->nullable();
21+
$table->timestamp('expires_at')->nullable();
22+
$table->timestamps();
23+
});
24+
}
25+
26+
/**
27+
* Reverse the migrations.
28+
*/
29+
public function down(): void
30+
{
31+
Schema::dropIfExists('personal_access_tokens');
32+
}
33+
};

resources/js/Components/BlockSchedule/BlockScheduleManager.jsx

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import Modal from '../Modal';
66
import Form from './Form';
77
import Select from './Select';
88
import { Icon } from '@iconify/react';
9-
import axios from 'axios';
10-
import { usePage, router } from '@inertiajs/react';
9+
import { usePage } from '@inertiajs/react';
10+
import DataService from '../../services/data-service';
1111

1212
const BlockScheduleManager = () => {
1313
const { auth } = usePage().props;
@@ -20,24 +20,24 @@ const BlockScheduleManager = () => {
2020
const [services, setServices] = useState([]);
2121
const [rooms, setRooms] = useState([]);
2222
useEffect(() => {
23-
if (!auth.user) {
24-
router.visit('/login');
25-
return;
26-
}
27-
2823
const fetchData = async () => {
2924
try {
30-
const [blocksRes, utilizationRes, servicesRes, roomsRes] = await Promise.all([
31-
axios.get('/api/blocks'),
32-
axios.get('/api/blocks/utilization'),
33-
axios.get('/api/services'),
34-
axios.get('/api/rooms')
25+
const [blocks, utilization, services] = await Promise.all([
26+
DataService.getBlockTemplates(),
27+
DataService.getBlockUtilization(selectedDate.toISOString().split('T')[0]),
28+
DataService.getServices()
3529
]);
3630

37-
setBlocks(blocksRes.data);
38-
setUtilization(utilizationRes.data);
39-
setServices(servicesRes.data);
40-
setRooms(roomsRes.data);
31+
setBlocks(blocks);
32+
setUtilization(utilization);
33+
setServices(services);
34+
setRooms([
35+
{ room_id: 1, name: 'OR-1' },
36+
{ room_id: 2, name: 'OR-2' },
37+
{ room_id: 3, name: 'OR-3' },
38+
{ room_id: 4, name: 'OR-4' },
39+
{ room_id: 5, name: 'OR-5' }
40+
]);
4141
setError(null);
4242
} catch (err) {
4343
console.error('Error fetching data:', err);
@@ -48,7 +48,7 @@ const BlockScheduleManager = () => {
4848
};
4949

5050
fetchData();
51-
}, []);
51+
}, [selectedDate]);
5252

5353
const getBlocksForDate = (date) => {
5454
return blocks.filter(block =>
@@ -80,13 +80,19 @@ const BlockScheduleManager = () => {
8080
const handleSubmit = async (e) => {
8181
e.preventDefault();
8282
try {
83-
await axios.post('/api/blocks', formData);
84-
const [blocksRes, utilizationRes] = await Promise.all([
85-
axios.get('/api/blocks'),
86-
axios.get('/api/blocks/utilization')
87-
]);
88-
setBlocks(blocksRes.data);
89-
setUtilization(utilizationRes.data);
83+
// In mock mode, just update the local state with new block
84+
const newBlock = {
85+
block_id: blocks.length + 1,
86+
room_id: formData.room_id,
87+
service_id: formData.service_id,
88+
service_name: services.find(s => s.service_id === formData.service_id)?.name,
89+
block_date: formData.block_date,
90+
start_time: formData.start_time,
91+
end_time: formData.end_time,
92+
title: `${services.find(s => s.service_id === formData.service_id)?.name} Block`
93+
};
94+
95+
setBlocks([...blocks, newBlock]);
9096
setShowModal(false);
9197
setFormData({
9298
service_id: '',

resources/js/Components/Dashboard/DashboardOverview.jsx

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import Card from './Card';
33
import Stats from './Stats';
44
import { Icon } from '@iconify/react';
55
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
6-
import axios from 'axios';
7-
import { usePage, router } from '@inertiajs/react';
6+
import { usePage } from '@inertiajs/react';
7+
import DataService from '../../services/data-service';
88

99
const DashboardOverview = () => {
1010
const { auth } = usePage().props;
@@ -15,22 +15,17 @@ const DashboardOverview = () => {
1515
const [error, setError] = useState(null);
1616

1717
useEffect(() => {
18-
if (!auth.user) {
19-
router.visit('/login');
20-
return;
21-
}
22-
2318
const fetchDashboardData = async () => {
2419
try {
25-
const [metricsRes, casesRes, roomsRes] = await Promise.all([
26-
axios.get('/api/cases/metrics'),
27-
axios.get('/api/cases/today'),
28-
axios.get('/api/cases/room-status')
20+
const [metrics, cases, rooms] = await Promise.all([
21+
DataService.getDashboardMetrics(),
22+
DataService.getTodaysCases(),
23+
DataService.getRoomStatus()
2924
]);
3025

31-
setMetrics(metricsRes.data);
32-
setTodaysCases(casesRes.data);
33-
setRoomStatus(roomsRes.data);
26+
setMetrics(metrics);
27+
setTodaysCases(cases);
28+
setRoomStatus(rooms);
3429
setError(null);
3530
} catch (err) {
3631
console.error('Error fetching dashboard data:', err);

resources/js/app.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import './bootstrap';
44
import { createInertiaApp } from '@inertiajs/react';
55
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
66
import { createRoot } from 'react-dom/client';
7+
import { HeroUIProvider } from '@heroui/react';
78
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
89

910
createInertiaApp({
@@ -15,7 +16,11 @@ createInertiaApp({
1516
),
1617
setup({ el, App, props }) {
1718
const root = createRoot(el);
18-
root.render(<App {...props} />);
19+
root.render(
20+
<HeroUIProvider>
21+
<App {...props} />
22+
</HeroUIProvider>
23+
);
1924
},
2025
progress: {
2126
color: '#4B5563',

0 commit comments

Comments
 (0)