Feature: Tenant Short Name and Branding Implementation
- Added short_name to Tenant model and controller - Updated Landlord/Tenant pages (Create, Edit, Show, Index) - Implemented branding customization (Favicon, Login Copyright, Sidebar Title) - Updated HandleInertiaRequests to share branding data
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Head, useForm } from "@inertiajs/react";
|
||||
import { Head, useForm, usePage } from "@inertiajs/react";
|
||||
import { PageProps } from "@/types/global";
|
||||
import { FormEventHandler, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
@@ -8,6 +9,7 @@ import InputError from "../../Components/InputError";
|
||||
import ApplicationLogo from "../../Components/ApplicationLogo";
|
||||
|
||||
export default function Login() {
|
||||
const { props } = usePage<PageProps>();
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
username: localStorage.getItem("saved_username") || "",
|
||||
password: "",
|
||||
@@ -134,7 +136,7 @@ export default function Login() {
|
||||
</div>
|
||||
|
||||
<p className="text-center text-gray-400 text-sm mt-8">
|
||||
© 2026 小小冰室. All rights reserved.
|
||||
© {new Date().getFullYear()} {props.branding?.name || '小小冰室'}. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ export default function TenantCreate() {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
id: "",
|
||||
name: "",
|
||||
short_name: "",
|
||||
email: "",
|
||||
domain: "",
|
||||
});
|
||||
@@ -55,6 +56,21 @@ export default function TenantCreate() {
|
||||
{errors.name && <p className="mt-1 text-sm text-red-500">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">
|
||||
客戶簡稱
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.short_name}
|
||||
onChange={(e) => setData("short_name", e.target.value)}
|
||||
placeholder="例如:小冰"
|
||||
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
||||
/>
|
||||
<p className="mt-1 text-sm text-slate-500">選填</p>
|
||||
{errors.short_name && <p className="mt-1 text-sm text-red-500">{errors.short_name}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">
|
||||
聯絡信箱
|
||||
|
||||
@@ -5,6 +5,7 @@ import { FormEvent } from "react";
|
||||
interface Tenant {
|
||||
id: string;
|
||||
name: string;
|
||||
short_name: string | null;
|
||||
email: string | null;
|
||||
is_active: boolean;
|
||||
}
|
||||
@@ -16,6 +17,7 @@ interface Props {
|
||||
export default function TenantEdit({ tenant }: Props) {
|
||||
const { data, setData, put, processing, errors } = useForm({
|
||||
name: tenant.name,
|
||||
short_name: tenant.short_name || "",
|
||||
email: tenant.email || "",
|
||||
is_active: tenant.is_active,
|
||||
});
|
||||
@@ -62,6 +64,19 @@ export default function TenantEdit({ tenant }: Props) {
|
||||
{errors.name && <p className="mt-1 text-sm text-red-500">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">
|
||||
客戶簡稱
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.short_name}
|
||||
onChange={(e) => setData("short_name", e.target.value)}
|
||||
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
||||
/>
|
||||
<p className="mt-1 text-sm text-slate-500">選填</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">
|
||||
聯絡信箱
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
interface Tenant {
|
||||
id: string;
|
||||
name: string;
|
||||
short_name: string | null;
|
||||
email: string | null;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
@@ -67,6 +68,9 @@ export default function TenantIndex({ tenants }: Props) {
|
||||
<th className="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase">
|
||||
名稱
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase">
|
||||
簡稱
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-semibold text-slate-500 uppercase">
|
||||
域名
|
||||
</th>
|
||||
@@ -84,7 +88,7 @@ export default function TenantIndex({ tenants }: Props) {
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{tenants.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={6} className="px-6 py-12 text-center text-slate-500">
|
||||
<td colSpan={7} className="px-6 py-12 text-center text-slate-500">
|
||||
尚無客戶資料,請點擊「新增客戶」建立第一個客戶
|
||||
</td>
|
||||
</tr>
|
||||
@@ -102,6 +106,9 @@ export default function TenantIndex({ tenants }: Props) {
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-slate-700">
|
||||
{tenant.short_name || '-'}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
{tenant.domains.length > 0 ? (
|
||||
<div className="flex items-center gap-1 flex-wrap">
|
||||
|
||||
@@ -11,6 +11,7 @@ interface Domain {
|
||||
interface Tenant {
|
||||
id: string;
|
||||
name: string;
|
||||
short_name: string | null;
|
||||
email: string | null;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
@@ -97,6 +98,10 @@ export default function TenantShow({ tenant }: Props) {
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-slate-500">客戶簡稱</dt>
|
||||
<dd className="mt-1 text-slate-900">{tenant.short_name || "-"}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-slate-500">聯絡信箱</dt>
|
||||
<dd className="mt-1 text-slate-900">{tenant.email || "-"}</dd>
|
||||
|
||||
Reference in New Issue
Block a user