diff --git a/app/Http/Controllers/Landlord/ProfileController.php b/app/Http/Controllers/Landlord/ProfileController.php
new file mode 100644
index 0000000..7bfc423
--- /dev/null
+++ b/app/Http/Controllers/Landlord/ProfileController.php
@@ -0,0 +1,55 @@
+ $request->user(),
+ ]);
+ }
+
+ /**
+ * 更新使用者基本資料
+ */
+ public function update(Request $request)
+ {
+ $validated = $request->validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'username' => ['required', 'string', 'max:255', 'unique:users,username,' . $request->user()->id],
+ 'email' => ['nullable', 'string', 'email', 'max:255', 'unique:users,email,' . $request->user()->id],
+ ]);
+
+ $request->user()->update($validated);
+
+ return back()->with('success', '個人資料已更新');
+ }
+
+ /**
+ * 更新密碼
+ */
+ public function updatePassword(Request $request)
+ {
+ $validated = $request->validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', 'confirmed', Password::defaults()],
+ ]);
+
+ $request->user()->update([
+ 'password' => Hash::make($validated['password']),
+ ]);
+
+ return back()->with('success', '密碼已更新');
+ }
+}
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
new file mode 100644
index 0000000..d95076d
--- /dev/null
+++ b/app/Http/Controllers/ProfileController.php
@@ -0,0 +1,54 @@
+ $request->user(),
+ ]);
+ }
+
+ /**
+ * 更新使用者基本資料
+ */
+ public function update(Request $request)
+ {
+ $validated = $request->validate([
+ 'name' => ['required', 'string', 'max:255'],
+ 'username' => ['required', 'string', 'max:255', 'unique:users,username,' . $request->user()->id],
+ 'email' => ['nullable', 'string', 'email', 'max:255', 'unique:users,email,' . $request->user()->id],
+ ]);
+
+ $request->user()->update($validated);
+
+ return back()->with('success', '個人資料已更新');
+ }
+
+ /**
+ * 更新密碼
+ */
+ public function updatePassword(Request $request)
+ {
+ $validated = $request->validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', 'confirmed', Password::defaults()],
+ ]);
+
+ $request->user()->update([
+ 'password' => Hash::make($validated['password']),
+ ]);
+
+ return back()->with('success', '密碼已更新');
+ }
+}
diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php
index 3e13687..231a939 100644
--- a/app/Http/Middleware/HandleInertiaRequests.php
+++ b/app/Http/Middleware/HandleInertiaRequests.php
@@ -47,6 +47,7 @@ class HandleInertiaRequests extends Middleware
'username' => $user->username ?? null,
// 權限資料
'roles' => $user->getRoleNames(),
+ 'role_labels' => $user->roles->pluck('display_name'),
'permissions' => $user->getAllPermissions()->pluck('name')->toArray(),
] : null,
],
diff --git a/bootstrap/app.php b/bootstrap/app.php
index 73e4de9..3721148 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -18,8 +18,11 @@ return Application::configure(basePath: dirname(__DIR__))
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
- $middleware->web(append: [
+ // Tenancy 必須最先執行,確保資料庫連線在 Session 讀取之前建立
+ $middleware->web(prepend: [
\App\Http\Middleware\UniversalTenancy::class,
+ ]);
+ $middleware->web(append: [
\App\Http\Middleware\HandleInertiaRequests::class,
]);
diff --git a/resources/js/Layouts/AuthenticatedLayout.tsx b/resources/js/Layouts/AuthenticatedLayout.tsx
index 914f552..8415661 100644
--- a/resources/js/Layouts/AuthenticatedLayout.tsx
+++ b/resources/js/Layouts/AuthenticatedLayout.tsx
@@ -351,7 +351,7 @@ export default function AuthenticatedLayout({
{user.name}
- {user.username || 'Administrator'}
+ {user.role_labels?.[0] || user.roles?.[0] || '一般用戶'}
@@ -359,7 +359,17 @@ export default function AuthenticatedLayout({
- 我的帳號
+ {user.name} ({user.username})
+
+
+
+
+ 使用者設定
+
+
-
+
{user.name}
- 系統管理員
+
+ {user.role_labels?.[0] || user.roles?.[0] || '系統管理員'}
+
-
-
+
+
-
- 我的帳號
+
+ {user.name} ({user.username})
+
+
+
+
+ 使用者設定
+
+
登出系統
diff --git a/resources/js/Pages/Landlord/Dashboard.tsx b/resources/js/Pages/Landlord/Dashboard.tsx
index 998f067..14c76dc 100644
--- a/resources/js/Pages/Landlord/Dashboard.tsx
+++ b/resources/js/Pages/Landlord/Dashboard.tsx
@@ -39,7 +39,9 @@ export default function Dashboard({ totalTenants, activeTenants, recentTenants }
];
return (
-
+
{/* Stats Cards */}
diff --git a/resources/js/Pages/Landlord/Profile/Edit.tsx b/resources/js/Pages/Landlord/Profile/Edit.tsx
new file mode 100644
index 0000000..0891bb5
--- /dev/null
+++ b/resources/js/Pages/Landlord/Profile/Edit.tsx
@@ -0,0 +1,195 @@
+import LandlordLayout from "@/Layouts/LandlordLayout";
+import { Head, useForm } from "@inertiajs/react";
+import { User, Lock, Mail } from "lucide-react";
+import { FormEvent } from "react";
+import { toast } from "sonner";
+
+interface User {
+ id: number;
+ name: string;
+ email: string;
+ username: string;
+}
+
+interface Props {
+ user: User;
+}
+
+export default function Edit({ user }: Props) {
+ // 個人資料表單
+ const { data: profileData, setData: setProfileData, patch: patchProfile, processing: profileProcessing, errors: profileErrors } = useForm({
+ name: user.name,
+ username: user.username || "",
+ email: user.email || "",
+ });
+
+ // 密碼表單
+ const { data: passwordData, setData: setPasswordData, put: putPassword, processing: passwordProcessing, errors: passwordErrors, reset: resetPassword } = useForm({
+ current_password: "",
+ password: "",
+ password_confirmation: "",
+ });
+
+ const handleProfileSubmit = (e: FormEvent) => {
+ e.preventDefault();
+ patchProfile(route('landlord.profile.update'), {
+ onSuccess: () => toast.success('個人資料已更新'),
+ onError: () => toast.error('更新失敗,請檢查輸入內容'),
+ });
+ };
+
+ const handlePasswordSubmit = (e: FormEvent) => {
+ e.preventDefault();
+ putPassword(route('landlord.profile.password'), {
+ onSuccess: () => {
+ toast.success('密碼已更新');
+ resetPassword();
+ },
+ onError: () => toast.error('密碼更新失敗'),
+ });
+ };
+
+ return (
+
+
+
+
+ {/* 頁面標題 */}
+
+
+
+ 使用者設定
+
+
管理您的個人資料與帳號安全
+
+
+ {/* 個人資料區塊 */}
+
+
+ {/* 密碼變更區塊 */}
+
+
+
+ );
+}
diff --git a/resources/js/Pages/Landlord/Tenant/Create.tsx b/resources/js/Pages/Landlord/Tenant/Create.tsx
index 75a2778..47277b3 100644
--- a/resources/js/Pages/Landlord/Tenant/Create.tsx
+++ b/resources/js/Pages/Landlord/Tenant/Create.tsx
@@ -16,7 +16,9 @@ export default function TenantCreate() {
};
return (
-
+
新增客戶
diff --git a/resources/js/Pages/Landlord/Tenant/Edit.tsx b/resources/js/Pages/Landlord/Tenant/Edit.tsx
index 92eb2e0..d580755 100644
--- a/resources/js/Pages/Landlord/Tenant/Edit.tsx
+++ b/resources/js/Pages/Landlord/Tenant/Edit.tsx
@@ -26,7 +26,9 @@ export default function TenantEdit({ tenant }: Props) {
};
return (
-
+
編輯客戶
diff --git a/resources/js/Pages/Landlord/Tenant/Index.tsx b/resources/js/Pages/Landlord/Tenant/Index.tsx
index 1d6d064..bd4f5fd 100644
--- a/resources/js/Pages/Landlord/Tenant/Index.tsx
+++ b/resources/js/Pages/Landlord/Tenant/Index.tsx
@@ -37,7 +37,9 @@ export default function TenantIndex({ tenants }: Props) {
};
return (
-
+
{/* Header */}
diff --git a/resources/js/Pages/Landlord/Tenant/Show.tsx b/resources/js/Pages/Landlord/Tenant/Show.tsx
index b2b39b1..efb5673 100644
--- a/resources/js/Pages/Landlord/Tenant/Show.tsx
+++ b/resources/js/Pages/Landlord/Tenant/Show.tsx
@@ -45,7 +45,9 @@ export default function TenantShow({ tenant }: Props) {
};
return (
-
+
{/* Back Link */}
{
+ e.preventDefault();
+ patchProfile(route('profile.update'), {
+ onSuccess: () => toast.success('個人資料已更新'),
+ onError: () => toast.error('更新失敗,請檢查輸入內容'),
+ });
+ };
+
+ const handlePasswordSubmit = (e: FormEvent) => {
+ e.preventDefault();
+ putPassword(route('profile.password'), {
+ onSuccess: () => {
+ toast.success('密碼已更新');
+ resetPassword();
+ },
+ onError: () => toast.error('密碼更新失敗'),
+ });
+ };
+
+ return (
+
+
+
+
+ {/* 頁面標題 */}
+
+
+
+ 使用者設定
+
+
管理您的個人資料與帳號安全
+
+
+
+ {/* 個人資料區塊 */}
+
+
+ {/* 密碼變更區塊 */}
+
+
+
+
+ );
+}
diff --git a/resources/js/types/global.d.ts b/resources/js/types/global.d.ts
index 3854a8b..f352836 100644
--- a/resources/js/types/global.d.ts
+++ b/resources/js/types/global.d.ts
@@ -7,6 +7,7 @@ export interface AuthUser {
email: string;
username?: string;
roles: string[];
+ role_labels: string[];
permissions: string[];
}
diff --git a/routes/landlord.php b/routes/landlord.php
index 45f2b41..699e17b 100644
--- a/routes/landlord.php
+++ b/routes/landlord.php
@@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Landlord\DashboardController;
use App\Http\Controllers\Landlord\TenantController;
+use App\Http\Controllers\Landlord\ProfileController;
/*
|--------------------------------------------------------------------------
@@ -21,6 +22,11 @@ Route::prefix('landlord')->name('landlord.')->middleware(['web', 'auth', \App\Ht
// 租戶管理 CRUD
Route::resource('tenants', TenantController::class);
+ // 使用者設定
+ Route::get('profile', [ProfileController::class, 'edit'])->name('profile.edit');
+ Route::patch('profile', [ProfileController::class, 'update'])->name('profile.update');
+ Route::put('profile/password', [ProfileController::class, 'updatePassword'])->name('profile.password');
+
// 租戶域名管理
Route::post('tenants/{tenant}/domains', [TenantController::class, 'addDomain'])->name('tenants.domains.store');
Route::delete('tenants/{tenant}/domains/{domain}', [TenantController::class, 'removeDomain'])->name('tenants.domains.destroy');
diff --git a/routes/web.php b/routes/web.php
index b11c5e5..02f6616 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -16,6 +16,7 @@ use App\Http\Controllers\TransferOrderController;
use App\Http\Controllers\UnitController;
use App\Http\Controllers\Admin\RoleController;
use App\Http\Controllers\Admin\UserController;
+use App\Http\Controllers\ProfileController;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
// 登入/登出路由
@@ -27,6 +28,11 @@ Route::middleware('auth')->group(function () {
// 儀表板 - 所有登入使用者皆可存取
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
+ // 使用者帳號設定
+ Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
+ Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
+ Route::put('/profile/password', [ProfileController::class, 'updatePassword'])->name('profile.password');
+
// 類別管理 (用於商品對話框) - 需要商品權限
Route::middleware('permission:products.view')->group(function () {
Route::get('/categories', [CategoryController::class, 'index'])->name('categories.index');