feat: 實作 Multi-tenancy 多租戶架構 (stancl/tenancy)
- 安裝並設定 stancl/tenancy 套件 - 分離 Central / Tenant migrations - 建立 Tenant Model 與資料遷移指令 - 建立房東後台 CRUD (Landlord Dashboard) - 新增租戶管理頁面 (列表、新增、編輯、詳情) - 新增域名管理功能 - 更新部署手冊
This commit is contained in:
106
resources/js/Pages/Landlord/Dashboard.tsx
Normal file
106
resources/js/Pages/Landlord/Dashboard.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import LandlordLayout from "@/Layouts/LandlordLayout";
|
||||
import { Building2, Users, Activity } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
interface Tenant {
|
||||
id: string;
|
||||
name: string;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
domains: string[];
|
||||
}
|
||||
|
||||
interface DashboardProps {
|
||||
totalTenants: number;
|
||||
activeTenants: number;
|
||||
recentTenants: Tenant[];
|
||||
}
|
||||
|
||||
export default function Dashboard({ totalTenants, activeTenants, recentTenants }: DashboardProps) {
|
||||
const statsCards = [
|
||||
{
|
||||
title: "租戶總數",
|
||||
value: totalTenants,
|
||||
icon: Building2,
|
||||
color: "bg-blue-500",
|
||||
},
|
||||
{
|
||||
title: "啟用中",
|
||||
value: activeTenants,
|
||||
icon: Activity,
|
||||
color: "bg-green-500",
|
||||
},
|
||||
{
|
||||
title: "停用中",
|
||||
value: totalTenants - activeTenants,
|
||||
icon: Users,
|
||||
color: "bg-slate-400",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<LandlordLayout title="儀表板">
|
||||
<div className="space-y-6">
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{statsCards.map((stat) => (
|
||||
<div
|
||||
key={stat.title}
|
||||
className="bg-white rounded-xl border border-slate-200 p-6 flex items-center gap-4"
|
||||
>
|
||||
<div className={`w-12 h-12 ${stat.color} rounded-lg flex items-center justify-center`}>
|
||||
<stat.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-500">{stat.title}</p>
|
||||
<p className="text-2xl font-bold text-slate-900">{stat.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Recent Tenants */}
|
||||
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-slate-200 flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-slate-900">最近新增的租戶</h2>
|
||||
<Link
|
||||
href="/landlord/tenants"
|
||||
className="text-sm text-primary-main hover:underline"
|
||||
>
|
||||
查看全部 →
|
||||
</Link>
|
||||
</div>
|
||||
<div className="divide-y divide-slate-100">
|
||||
{recentTenants.length === 0 ? (
|
||||
<div className="px-6 py-8 text-center text-slate-500">
|
||||
尚無租戶資料
|
||||
</div>
|
||||
) : (
|
||||
recentTenants.map((tenant) => (
|
||||
<div key={tenant.id} className="px-6 py-4 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-slate-900">{tenant.name}</p>
|
||||
<p className="text-sm text-slate-500">
|
||||
{tenant.domains.length > 0 ? tenant.domains.join(", ") : "無綁定域名"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium ${tenant.is_active
|
||||
? "bg-green-100 text-green-700"
|
||||
: "bg-slate-100 text-slate-600"
|
||||
}`}
|
||||
>
|
||||
{tenant.is_active ? "啟用" : "停用"}
|
||||
</span>
|
||||
<span className="text-sm text-slate-400">{tenant.created_at}</span>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LandlordLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user