197 lines
8.9 KiB
TypeScript
197 lines
8.9 KiB
TypeScript
|
|
import LandlordLayout from "@/Layouts/LandlordLayout";
|
|||
|
|
import { Link, useForm } from "@inertiajs/react";
|
|||
|
|
import { Button } from "@/Components/ui/button";
|
|||
|
|
import { Input } from "@/Components/ui/input";
|
|||
|
|
import { Label } from "@/Components/ui/label";
|
|||
|
|
import { Card } from "@/Components/ui/card";
|
|||
|
|
import { ArrowLeft, LayoutDashboard } from "lucide-react";
|
|||
|
|
import { FormEventHandler, useState } from "react";
|
|||
|
|
import { toast } from "sonner";
|
|||
|
|
import { generateLightestColor } from "@/utils/colorUtils";
|
|||
|
|
|
|||
|
|
interface Tenant {
|
|||
|
|
id: string;
|
|||
|
|
name: string;
|
|||
|
|
branding?: {
|
|||
|
|
logo_path?: string;
|
|||
|
|
primary_color?: string;
|
|||
|
|
text_color?: string;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface BrandingProps {
|
|||
|
|
tenant: Tenant;
|
|||
|
|
logo_url?: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function Branding({ tenant, logo_url }: BrandingProps) {
|
|||
|
|
const { data, setData, post, processing, errors } = useForm({
|
|||
|
|
logo: null as File | null,
|
|||
|
|
primary_color: tenant.branding?.primary_color || '#01ab83',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const [logoPreview, setLogoPreview] = useState<string | null>(logo_url || null);
|
|||
|
|
|
|||
|
|
const handleLogoChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|||
|
|
const file = e.target.files?.[0];
|
|||
|
|
if (file) {
|
|||
|
|
setData('logo', file);
|
|||
|
|
const reader = new FileReader();
|
|||
|
|
reader.onloadend = () => {
|
|||
|
|
setLogoPreview(reader.result as string);
|
|||
|
|
};
|
|||
|
|
reader.readAsDataURL(file);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const submit: FormEventHandler = (e) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
post(route('landlord.tenants.branding.update', tenant.id), {
|
|||
|
|
onSuccess: () => {
|
|||
|
|
toast.success('樣式設定已更新');
|
|||
|
|
},
|
|||
|
|
onError: () => {
|
|||
|
|
toast.error('更新失敗,請檢查輸入');
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<LandlordLayout title="客戶樣式管理">
|
|||
|
|
<div className="space-y-6 p-6">
|
|||
|
|
{/* Header */}
|
|||
|
|
<div className="flex items-center justify-between">
|
|||
|
|
<div className="flex items-center gap-4">
|
|||
|
|
<Link href={route('landlord.tenants.show', tenant.id)}>
|
|||
|
|
<Button variant="outline" size="sm">
|
|||
|
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
|||
|
|
返回
|
|||
|
|
</Button>
|
|||
|
|
</Link>
|
|||
|
|
<div>
|
|||
|
|
<h2 className="text-2xl font-bold text-slate-900">
|
|||
|
|
客戶樣式管理
|
|||
|
|
</h2>
|
|||
|
|
<p className="text-sm text-slate-500 mt-1">
|
|||
|
|
{tenant.name}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Branding Form */}
|
|||
|
|
<Card className="p-6">
|
|||
|
|
<form onSubmit={submit} className="space-y-6">
|
|||
|
|
{/* Logo Upload */}
|
|||
|
|
<div>
|
|||
|
|
<Label htmlFor="logo" className="text-base font-semibold">
|
|||
|
|
客戶 Logo
|
|||
|
|
</Label>
|
|||
|
|
<p className="text-sm text-slate-500 mb-3">
|
|||
|
|
上傳客戶專屬 Logo,建議尺寸 200×200 px,最大 2MB
|
|||
|
|
</p>
|
|||
|
|
<Input
|
|||
|
|
id="logo"
|
|||
|
|
type="file"
|
|||
|
|
accept="image/*"
|
|||
|
|
onChange={handleLogoChange}
|
|||
|
|
className="cursor-pointer"
|
|||
|
|
/>
|
|||
|
|
{errors.logo && (
|
|||
|
|
<p className="text-sm text-red-600 mt-1">{errors.logo}</p>
|
|||
|
|
)}
|
|||
|
|
{logoPreview && (
|
|||
|
|
<div className="mt-4 p-4 bg-slate-50 rounded-lg border border-slate-200">
|
|||
|
|
<p className="text-sm text-slate-600 mb-2">預覽:</p>
|
|||
|
|
<img
|
|||
|
|
src={logoPreview}
|
|||
|
|
alt="Logo Preview"
|
|||
|
|
className="h-20 w-20 object-contain rounded-lg border border-slate-300"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Primary Color */}
|
|||
|
|
<div>
|
|||
|
|
<Label htmlFor="primary_color" className="text-base font-semibold">
|
|||
|
|
主色系
|
|||
|
|
</Label>
|
|||
|
|
<p className="text-sm text-slate-500 mb-3">
|
|||
|
|
設定客戶後台的主要品牌色(按鈕、連結等)
|
|||
|
|
</p>
|
|||
|
|
<div className="flex items-center gap-3">
|
|||
|
|
<Input
|
|||
|
|
id="primary_color"
|
|||
|
|
type="color"
|
|||
|
|
value={data.primary_color}
|
|||
|
|
onChange={(e) => setData('primary_color', e.target.value)}
|
|||
|
|
className="w-16 h-10 cursor-pointer"
|
|||
|
|
/>
|
|||
|
|
<Input
|
|||
|
|
type="text"
|
|||
|
|
value={data.primary_color}
|
|||
|
|
onChange={(e) => setData('primary_color', e.target.value)}
|
|||
|
|
pattern="^#[0-9A-Fa-f]{6}$"
|
|||
|
|
className="w-32"
|
|||
|
|
placeholder="#01ab83"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
{errors.primary_color && (
|
|||
|
|
<p className="text-sm text-red-600 mt-1">{errors.primary_color}</p>
|
|||
|
|
)}
|
|||
|
|
{/* Preview */}
|
|||
|
|
<div className="mt-4 p-4 bg-slate-50 rounded-lg border border-slate-200">
|
|||
|
|
<p className="text-sm text-slate-600 mb-3">預覽效果:</p>
|
|||
|
|
<div className="flex flex-wrap gap-4">
|
|||
|
|
{/* 按鈕預覽 */}
|
|||
|
|
<div>
|
|||
|
|
<p className="text-xs text-slate-500 mb-2">按鈕</p>
|
|||
|
|
<Button
|
|||
|
|
type="button"
|
|||
|
|
style={{ backgroundColor: data.primary_color }}
|
|||
|
|
className="text-white"
|
|||
|
|
>
|
|||
|
|
預覽按鈕
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
{/* 側邊欄選中狀態預覽 */}
|
|||
|
|
<div>
|
|||
|
|
<p className="text-xs text-slate-500 mb-2">側邊欄選中狀態</p>
|
|||
|
|
<div
|
|||
|
|
className="flex items-center gap-2 px-3 py-2 rounded-lg"
|
|||
|
|
style={{
|
|||
|
|
backgroundColor: generateLightestColor(data.primary_color),
|
|||
|
|
color: data.primary_color
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<LayoutDashboard className="w-5 h-5" />
|
|||
|
|
<span className="font-medium">儀表板</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Submit Button */}
|
|||
|
|
<div className="flex justify-end gap-3 pt-4 border-t">
|
|||
|
|
<Link href={route('landlord.tenants.show', tenant.id)}>
|
|||
|
|
<Button type="button" variant="outline">
|
|||
|
|
取消
|
|||
|
|
</Button>
|
|||
|
|
</Link>
|
|||
|
|
<Button
|
|||
|
|
type="submit"
|
|||
|
|
disabled={processing}
|
|||
|
|
className="bg-primary-main hover:bg-primary-dark"
|
|||
|
|
>
|
|||
|
|
{processing ? '儲存中...' : '儲存樣式設定'}
|
|||
|
|
</Button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</Card>
|
|||
|
|
</div>
|
|||
|
|
</LandlordLayout>
|
|||
|
|
);
|
|||
|
|
}
|