Merge branch 'main' into feat/e2e-login-impersonation-gro-77
This commit is contained in:
@@ -22,7 +22,7 @@ export function AccountSettings({ readOnly }: Props) {
|
||||
key={id}
|
||||
onClick={() => setTab(id)}
|
||||
className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium ${
|
||||
tab === id ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"
|
||||
tab === id ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"
|
||||
}`}
|
||||
>
|
||||
<Icon size={14} />
|
||||
@@ -69,7 +69,7 @@ function PersonalInfo({ readOnly }: { readOnly: boolean }) {
|
||||
</div>
|
||||
))}
|
||||
{!readOnly && (
|
||||
<button className="px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]">
|
||||
<button className="px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)">
|
||||
Save Changes
|
||||
</button>
|
||||
)}
|
||||
@@ -103,7 +103,7 @@ function PasswordChange({ readOnly }: { readOnly: boolean }) {
|
||||
<label className="block text-sm font-medium text-stone-700 mb-1">Confirm New Password</label>
|
||||
<input type="password" className="w-full border border-stone-300 rounded-lg px-3 py-2 text-sm" />
|
||||
</div>
|
||||
<button className="px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]">
|
||||
<button className="px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)">
|
||||
Update Password
|
||||
</button>
|
||||
</div>
|
||||
@@ -116,7 +116,7 @@ function ManagePets({ readOnly }: { readOnly: boolean }) {
|
||||
<div className="space-y-4">
|
||||
{PETS.map(pet => (
|
||||
<div key={pet.id} className="bg-white rounded-2xl border border-stone-200 p-4 shadow-sm flex items-center gap-4">
|
||||
<div className="w-14 h-14 rounded-xl bg-[#f0ebe4] flex items-center justify-center text-3xl">
|
||||
<div className="w-14 h-14 rounded-xl bg-(--color-accent-light) flex items-center justify-center text-3xl">
|
||||
{pet.photo}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -136,7 +136,7 @@ function ManagePets({ readOnly }: { readOnly: boolean }) {
|
||||
</div>
|
||||
))}
|
||||
{!readOnly && (
|
||||
<button className="w-full flex items-center justify-center gap-2 py-3 border-2 border-dashed border-stone-300 rounded-2xl text-sm text-stone-500 hover:border-[#8b7355] hover:text-[#6b5a42] transition-colors">
|
||||
<button className="w-full flex items-center justify-center gap-2 py-3 border-2 border-dashed border-stone-300 rounded-2xl text-sm text-stone-500 hover:border-(--color-accent) hover:text-(--color-accent-dark) transition-colors">
|
||||
<Plus size={16} />
|
||||
Add New Pet
|
||||
</button>
|
||||
@@ -165,7 +165,7 @@ function Agreements() {
|
||||
{new Date(agr.dateSigned).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
|
||||
</td>
|
||||
<td className="px-5 py-3">
|
||||
<button className="text-sm text-[#6b5a42] font-medium hover:underline">View</button>
|
||||
<button className="text-sm text-(--color-accent-dark) font-medium hover:underline">View</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -30,13 +30,13 @@ export function AppointmentsSection({ readOnly }: Props) {
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setTab("upcoming")}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === "upcoming" ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"}`}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === "upcoming" ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"}`}
|
||||
>
|
||||
Upcoming ({UPCOMING_APPOINTMENTS.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTab("past")}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === "past" ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"}`}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === "past" ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"}`}
|
||||
>
|
||||
Past ({PAST_APPOINTMENTS.length})
|
||||
</button>
|
||||
@@ -44,7 +44,7 @@ export function AppointmentsSection({ readOnly }: Props) {
|
||||
{!readOnly && (
|
||||
<button
|
||||
onClick={() => setShowBooking(true)}
|
||||
className="flex items-center gap-1.5 px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]"
|
||||
className="flex items-center gap-1.5 px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Book New
|
||||
@@ -101,7 +101,7 @@ function AppointmentCard({
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-stone-200 shadow-sm overflow-hidden">
|
||||
<button onClick={onToggle} className="w-full flex items-center gap-4 p-4 text-left hover:bg-stone-50">
|
||||
<div className="w-10 h-10 rounded-lg bg-[#f0ebe4] flex items-center justify-center text-lg shrink-0">
|
||||
<div className="w-10 h-10 rounded-lg bg-(--color-accent-light) flex items-center justify-center text-lg shrink-0">
|
||||
{PETS.find(p => p.id === appt.petId)?.photo || "🐾"}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -150,7 +150,7 @@ function AppointmentCard({
|
||||
)}
|
||||
{appt.reportCardId && (
|
||||
<div className="mt-2">
|
||||
<span className="text-xs text-[#6b5a42] font-medium cursor-pointer hover:underline">
|
||||
<span className="text-xs text-(--color-accent-dark) font-medium cursor-pointer hover:underline">
|
||||
View Report Card →
|
||||
</span>
|
||||
</div>
|
||||
@@ -190,7 +190,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
{/* Step Indicator */}
|
||||
<div className="flex items-center gap-1 px-5 pt-4">
|
||||
{[1, 2, 3, 4, 5].map(s => (
|
||||
<div key={s} className={`flex-1 h-1.5 rounded-full ${s <= step ? "bg-[#8b7355]" : "bg-stone-200"}`} />
|
||||
<div key={s} className={`flex-1 h-1.5 rounded-full ${s <= step ? "bg-(--color-accent)" : "bg-stone-200"}`} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -202,7 +202,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
<p className="text-sm text-stone-500 mb-4">
|
||||
{selectedPet?.name} with {selectedGroomer?.name || "First Available"} on {formatDate(selectedDate)} at {selectedTime}
|
||||
</p>
|
||||
<button onClick={onClose} className="px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium">
|
||||
<button onClick={onClose} className="px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium">
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
@@ -218,7 +218,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
key={pet.id}
|
||||
onClick={() => { setSelectedPet(pet); setStep(2); }}
|
||||
className={`w-full flex items-center gap-3 p-3 rounded-xl border text-left transition-colors ${
|
||||
selectedPet?.id === pet.id ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 hover:border-stone-300"
|
||||
selectedPet?.id === pet.id ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl">{pet.photo}</span>
|
||||
@@ -246,7 +246,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
);
|
||||
}}
|
||||
className={`w-full flex items-center justify-between p-3 rounded-xl border text-left ${
|
||||
selectedServices.find(s => s.id === svc.id) ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 hover:border-stone-300"
|
||||
selectedServices.find(s => s.id === svc.id) ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
@@ -273,7 +273,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
);
|
||||
}}
|
||||
className={`w-full flex items-center justify-between p-2.5 rounded-lg border text-left text-sm ${
|
||||
selectedAddOns.find(s => s.id === svc.id) ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 hover:border-stone-300"
|
||||
selectedAddOns.find(s => s.id === svc.id) ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
@@ -291,7 +291,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
<button
|
||||
onClick={() => setStep(3)}
|
||||
disabled={selectedServices.length === 0}
|
||||
className="flex-1 px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium disabled:opacity-50"
|
||||
className="flex-1 px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
@@ -307,7 +307,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
<button
|
||||
onClick={() => { setSelectedGroomer(null); setStep(4); }}
|
||||
className={`w-full flex items-center gap-3 p-3 rounded-xl border text-left ${
|
||||
selectedGroomer === null ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 hover:border-stone-300"
|
||||
selectedGroomer === null ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full bg-stone-100 flex items-center justify-center">
|
||||
@@ -323,10 +323,10 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
key={g.id}
|
||||
onClick={() => { setSelectedGroomer(g); setStep(4); }}
|
||||
className={`w-full flex items-center gap-3 p-3 rounded-xl border text-left ${
|
||||
selectedGroomer?.id === g.id ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 hover:border-stone-300"
|
||||
selectedGroomer?.id === g.id ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<div className="w-10 h-10 rounded-full bg-[#f0ebe4] flex items-center justify-center text-xl">
|
||||
<div className="w-10 h-10 rounded-full bg-(--color-accent-light) flex items-center justify-center text-xl">
|
||||
{g.avatar}
|
||||
</div>
|
||||
<div>
|
||||
@@ -358,7 +358,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
key={time}
|
||||
onClick={() => setSelectedTime(time)}
|
||||
className={`px-3 py-2 rounded-lg text-sm border ${
|
||||
selectedTime === time ? "border-[#8b7355] bg-[#faf5ef] font-medium" : "border-stone-200 hover:border-stone-300"
|
||||
selectedTime === time ? "border-(--color-accent) bg-(--color-accent-lighter) font-medium" : "border-stone-200 hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
{time}
|
||||
@@ -387,7 +387,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
<button
|
||||
onClick={() => setStep(5)}
|
||||
disabled={!selectedDate || !selectedTime}
|
||||
className="flex-1 px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium disabled:opacity-50"
|
||||
className="flex-1 px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium disabled:opacity-50"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
@@ -426,7 +426,7 @@ function BookingFlow({ onClose, readOnly }: { onClose: () => void; readOnly: boo
|
||||
<button onClick={() => setStep(4)} className="flex-1 px-4 py-2 border border-stone-200 rounded-lg text-sm">Back</button>
|
||||
<button
|
||||
onClick={() => setConfirmed(true)}
|
||||
className="flex-1 px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]"
|
||||
className="flex-1 px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)"
|
||||
>
|
||||
Confirm Booking
|
||||
</button>
|
||||
|
||||
@@ -38,7 +38,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
>
|
||||
Add Tip
|
||||
</button>
|
||||
<button className="px-6 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]">
|
||||
<button className="px-6 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)">
|
||||
Pay Now
|
||||
</button>
|
||||
</div>
|
||||
@@ -57,7 +57,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
key={id}
|
||||
onClick={() => setTab(id)}
|
||||
className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium ${
|
||||
tab === id ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"
|
||||
tab === id ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"
|
||||
}`}
|
||||
>
|
||||
<Icon size={14} />
|
||||
@@ -134,7 +134,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
</div>
|
||||
))}
|
||||
{!readOnly && (
|
||||
<button className="flex items-center gap-2 text-sm text-[#6b5a42] font-medium hover:underline mt-2">
|
||||
<button className="flex items-center gap-2 text-sm text-(--color-accent-dark) font-medium hover:underline mt-2">
|
||||
<Plus size={14} />
|
||||
Add Payment Method
|
||||
</button>
|
||||
@@ -145,8 +145,8 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
<div className="bg-white rounded-2xl border border-stone-200 p-5 shadow-sm">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-[#f0ebe4] flex items-center justify-center">
|
||||
<Zap size={18} className="text-[#8b7355]" />
|
||||
<div className="w-10 h-10 rounded-lg bg-(--color-accent-light) flex items-center justify-center">
|
||||
<Zap size={18} className="text-(--color-accent)" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-stone-800">Autopay</p>
|
||||
@@ -156,7 +156,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
{!readOnly ? (
|
||||
<button
|
||||
onClick={() => setAutopay(!autopay)}
|
||||
className={`w-12 h-6 rounded-full transition-colors ${autopay ? "bg-[#8b7355]" : "bg-stone-300"}`}
|
||||
className={`w-12 h-6 rounded-full transition-colors ${autopay ? "bg-(--color-accent)" : "bg-stone-300"}`}
|
||||
>
|
||||
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ${autopay ? "translate-x-6" : "translate-x-0.5"}`} />
|
||||
</button>
|
||||
@@ -174,7 +174,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
{PREPAID_PACKAGES.map(pkg => (
|
||||
<div key={pkg.id} className="bg-white rounded-2xl border border-stone-200 p-5 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Package size={20} className="text-[#8b7355]" />
|
||||
<Package size={20} className="text-(--color-accent)" />
|
||||
<h3 className="font-medium text-stone-800">{pkg.name}</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mb-3">
|
||||
@@ -184,7 +184,7 @@ export function BillingPayments({ readOnly }: Props) {
|
||||
</div>
|
||||
<div className="flex-1 bg-stone-100 rounded-full h-3 overflow-hidden">
|
||||
<div
|
||||
className="bg-[#8b7355] h-full rounded-full"
|
||||
className="bg-(--color-accent) h-full rounded-full"
|
||||
style={{ width: `${((pkg.totalCredits - pkg.usedCredits) / pkg.totalCredits) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -218,7 +218,7 @@ function TipModal({ onClose }: { onClose: () => void }) {
|
||||
key={pct}
|
||||
onClick={() => { setTipPercent(pct); setCustomTip(""); }}
|
||||
className={`flex-1 py-2 rounded-lg text-sm font-medium border ${
|
||||
tipPercent === pct ? "border-[#8b7355] bg-[#faf5ef] text-[#6b5a42]" : "border-stone-200 text-stone-600"
|
||||
tipPercent === pct ? "border-(--color-accent) bg-(--color-accent-lighter) text-(--color-accent-dark)" : "border-stone-200 text-stone-600"
|
||||
}`}
|
||||
>
|
||||
{pct}%
|
||||
@@ -227,7 +227,7 @@ function TipModal({ onClose }: { onClose: () => void }) {
|
||||
<button
|
||||
onClick={() => { setTipPercent(null); }}
|
||||
className={`flex-1 py-2 rounded-lg text-sm font-medium border ${
|
||||
tipPercent === null ? "border-[#8b7355] bg-[#faf5ef] text-[#6b5a42]" : "border-stone-200 text-stone-600"
|
||||
tipPercent === null ? "border-(--color-accent) bg-(--color-accent-lighter) text-(--color-accent-dark)" : "border-stone-200 text-stone-600"
|
||||
}`}
|
||||
>
|
||||
Custom
|
||||
@@ -244,7 +244,7 @@ function TipModal({ onClose }: { onClose: () => void }) {
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
<button onClick={onClose} className="flex-1 px-4 py-2 border border-stone-200 rounded-lg text-sm">Cancel</button>
|
||||
<button onClick={onClose} className="flex-1 px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium">Add Tip</button>
|
||||
<button onClick={onClose} className="flex-1 px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium">Add Tip</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ export function Communication({ readOnly }: Props) {
|
||||
<button
|
||||
onClick={() => setTab("messages")}
|
||||
className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium ${
|
||||
tab === "messages" ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"
|
||||
tab === "messages" ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"
|
||||
}`}
|
||||
>
|
||||
Messages
|
||||
@@ -24,7 +24,7 @@ export function Communication({ readOnly }: Props) {
|
||||
<button
|
||||
onClick={() => setTab("notifications")}
|
||||
className={`flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium ${
|
||||
tab === "notifications" ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:bg-stone-50"
|
||||
tab === "notifications" ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:bg-stone-50"
|
||||
}`}
|
||||
>
|
||||
<Bell size={14} />
|
||||
@@ -68,7 +68,7 @@ function MessageThread({ readOnly }: { readOnly: boolean }) {
|
||||
<div key={msg.id} className={`flex ${msg.sender === "customer" ? "justify-end" : "justify-start"}`}>
|
||||
<div className={`max-w-[80%] rounded-2xl px-4 py-2.5 ${
|
||||
msg.sender === "customer"
|
||||
? "bg-[#8b7355] text-white rounded-br-md"
|
||||
? "bg-(--color-accent) text-white rounded-br-md"
|
||||
: "bg-stone-100 text-stone-800 rounded-bl-md"
|
||||
}`}>
|
||||
<p className="text-sm">{msg.text}</p>
|
||||
@@ -95,12 +95,12 @@ function MessageThread({ readOnly }: { readOnly: boolean }) {
|
||||
onChange={e => setNewMessage(e.target.value)}
|
||||
onKeyDown={e => e.key === "Enter" && handleSend()}
|
||||
placeholder="Type a message..."
|
||||
className="flex-1 border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#8b7355]/30 focus:border-[#8b7355]"
|
||||
className="flex-1 border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-(--color-accent)/30 focus:border-(--color-accent)"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSend}
|
||||
disabled={!newMessage.trim()}
|
||||
className="px-4 py-2 bg-[#8b7355] text-white rounded-lg hover:bg-[#7a6549] disabled:opacity-50"
|
||||
className="px-4 py-2 bg-(--color-accent) text-white rounded-lg hover:bg-(--color-accent-hover) disabled:opacity-50"
|
||||
>
|
||||
<Send size={16} />
|
||||
</button>
|
||||
@@ -177,7 +177,7 @@ function NotificationPreferences({ readOnly }: { readOnly: boolean }) {
|
||||
onClick={() => toggle(cat.key, ch.key)}
|
||||
disabled={readOnly}
|
||||
className={`w-10 h-5 rounded-full transition-colors inline-block ${
|
||||
prefs[cat.key][ch.key] ? "bg-[#8b7355]" : "bg-stone-300"
|
||||
prefs[cat.key][ch.key] ? "bg-(--color-accent)" : "bg-stone-300"
|
||||
} ${readOnly ? "cursor-not-allowed opacity-60" : ""}`}
|
||||
>
|
||||
<div className={`w-4 h-4 bg-white rounded-full shadow transition-transform ${
|
||||
|
||||
@@ -42,7 +42,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
{nextAppt && (
|
||||
<div className="bg-white rounded-2xl border border-stone-200 p-5 shadow-sm">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-[#6b5a42]">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-(--color-accent-dark)">
|
||||
<Calendar size={16} />
|
||||
Next Appointment
|
||||
</div>
|
||||
@@ -71,7 +71,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center sm:text-right">
|
||||
<div className="text-3xl font-bold text-[#6b5a42]">{daysUntil(nextAppt.date)}</div>
|
||||
<div className="text-3xl font-bold text-(--color-accent-dark)">{daysUntil(nextAppt.date)}</div>
|
||||
<div className="text-xs text-stone-500">days away</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,7 +103,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
className="bg-white rounded-2xl border border-stone-200 p-4 shadow-sm text-left hover:border-stone-300 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-12 h-12 rounded-full bg-[#f0ebe4] flex items-center justify-center text-2xl">
|
||||
<div className="w-12 h-12 rounded-full bg-(--color-accent-light) flex items-center justify-center text-2xl">
|
||||
{pet.photo}
|
||||
</div>
|
||||
<div>
|
||||
@@ -128,14 +128,14 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
|
||||
{/* Loyalty Card */}
|
||||
<div className="bg-white rounded-2xl border border-stone-200 p-4 shadow-sm">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-[#6b5a42] mb-3">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-(--color-accent-dark) mb-3">
|
||||
<Star size={16} />
|
||||
Loyalty Rewards
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-stone-800">{LOYALTY.points} <span className="text-sm font-normal text-stone-500">pts</span></p>
|
||||
<div className="mt-2 bg-stone-100 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
className="bg-[#8b7355] h-full rounded-full transition-all"
|
||||
className="bg-(--color-accent) h-full rounded-full transition-all"
|
||||
style={{ width: `${(LOYALTY.points / LOYALTY.nextRewardAt) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -161,7 +161,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
{!readOnly && (
|
||||
<button
|
||||
onClick={() => onNavigate("billing")}
|
||||
className="px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]"
|
||||
className="px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)"
|
||||
>
|
||||
Pay Now
|
||||
</button>
|
||||
@@ -176,7 +176,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
<div className="space-y-2.5">
|
||||
{recentEvents.map(evt => (
|
||||
<div key={evt.id} className="flex items-center gap-3 text-sm">
|
||||
<div className={`w-2 h-2 rounded-full shrink-0 ${evt.type === "payment" ? "bg-green-400" : "bg-[#8b7355]"}`} />
|
||||
<div className={`w-2 h-2 rounded-full shrink-0 ${evt.type === "payment" ? "bg-green-400" : "bg-(--color-accent)"}`} />
|
||||
<span className="text-stone-600 flex-1">{evt.text}</span>
|
||||
<span className="text-xs text-stone-400">{formatDate(evt.date)}</span>
|
||||
</div>
|
||||
@@ -184,7 +184,7 @@ export function Dashboard({ onNavigate, readOnly }: Props) {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onNavigate("appointments")}
|
||||
className="flex items-center gap-1 text-sm text-[#6b5a42] font-medium mt-3 hover:text-[#8b7355]"
|
||||
className="flex items-center gap-1 text-sm text-(--color-accent-dark) font-medium mt-3 hover:text-(--color-accent)"
|
||||
>
|
||||
View all <ChevronRight size={14} />
|
||||
</button>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function PetProfiles({ readOnly }: Props) {
|
||||
key={p.id}
|
||||
onClick={() => { setSelectedPetId(p.id); setActiveTab("info"); }}
|
||||
className={`flex items-center gap-3 px-4 py-3 rounded-xl border transition-colors ${
|
||||
p.id === selectedPetId ? "border-[#8b7355] bg-[#faf5ef]" : "border-stone-200 bg-white hover:border-stone-300"
|
||||
p.id === selectedPetId ? "border-(--color-accent) bg-(--color-accent-lighter)" : "border-stone-200 bg-white hover:border-stone-300"
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl">{p.photo}</span>
|
||||
@@ -45,7 +45,7 @@ export function PetProfiles({ readOnly }: Props) {
|
||||
{/* Profile Header */}
|
||||
<div className="bg-white rounded-2xl border border-stone-200 p-5 shadow-sm">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-20 h-20 rounded-2xl bg-[#f0ebe4] flex items-center justify-center text-4xl">
|
||||
<div className="w-20 h-20 rounded-2xl bg-(--color-accent-light) flex items-center justify-center text-4xl">
|
||||
{pet.photo}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -74,7 +74,7 @@ export function PetProfiles({ readOnly }: Props) {
|
||||
key={id}
|
||||
onClick={() => setActiveTab(id)}
|
||||
className={`flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${
|
||||
activeTab === id ? "bg-[#f0ebe4] text-[#6b5a42]" : "text-stone-500 hover:text-stone-700"
|
||||
activeTab === id ? "bg-(--color-accent-light) text-(--color-accent-dark)" : "text-stone-500 hover:text-stone-700"
|
||||
}`}
|
||||
>
|
||||
<Icon size={14} />
|
||||
@@ -114,7 +114,7 @@ function BasicInfoTab({ pet, readOnly }: { pet: Pet; readOnly: boolean }) {
|
||||
<InfoRow label="Sex" value={pet.sex === "male" ? "Male" : "Female"} />
|
||||
<InfoRow label="Spayed/Neutered" value={pet.spayedNeutered ? "Yes" : "No"} />
|
||||
{!readOnly && (
|
||||
<button className="mt-4 text-sm text-[#6b5a42] font-medium hover:underline">
|
||||
<button className="mt-4 text-sm text-(--color-accent-dark) font-medium hover:underline">
|
||||
Upload Photo
|
||||
</button>
|
||||
)}
|
||||
@@ -148,7 +148,7 @@ function GroomingTab({ pet, readOnly }: { pet: Pet; readOnly: boolean }) {
|
||||
<InfoRow label="Sensitive Areas" value={pet.sensitiveAreas} />
|
||||
<InfoRow label="Standing Instructions" value={pet.standingInstructions} />
|
||||
{!readOnly && (
|
||||
<button className="mt-4 text-sm text-[#6b5a42] font-medium hover:underline">
|
||||
<button className="mt-4 text-sm text-(--color-accent-dark) font-medium hover:underline">
|
||||
Upload Reference Photo
|
||||
</button>
|
||||
)}
|
||||
@@ -189,7 +189,7 @@ function VaccinationsTab({ pet, readOnly }: { pet: Pet; readOnly: boolean }) {
|
||||
{vax.documentUploaded ? (
|
||||
<span className="text-green-600 text-xs">Uploaded</span>
|
||||
) : !readOnly ? (
|
||||
<button className="flex items-center gap-1 text-xs text-[#6b5a42] hover:underline">
|
||||
<button className="flex items-center gap-1 text-xs text-(--color-accent-dark) hover:underline">
|
||||
<Upload size={12} />
|
||||
Upload
|
||||
</button>
|
||||
@@ -226,7 +226,7 @@ function HistoryTab({ petHistory }: { petHistory: typeof PAST_APPOINTMENTS }) {
|
||||
{new Date(appt.date).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
|
||||
</span>
|
||||
{appt.reportCardId && (
|
||||
<span className="text-xs text-[#6b5a42] font-medium">Report →</span>
|
||||
<span className="text-xs text-(--color-accent-dark) font-medium">Report →</span>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -33,7 +33,7 @@ export function ReportCards() {
|
||||
className="w-full bg-white rounded-2xl border border-stone-200 p-5 shadow-sm text-left hover:border-stone-300 transition-colors"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-14 h-14 rounded-xl bg-[#f0ebe4] flex items-center justify-center text-[#8b7355]">
|
||||
<div className="w-14 h-14 rounded-xl bg-(--color-accent-light) flex items-center justify-center text-(--color-accent)">
|
||||
<FileText size={24} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -70,13 +70,13 @@ function ReportCardDetail({ card, onBack }: { card: ReportCard; onBack: () => vo
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<button onClick={onBack} className="text-sm text-[#6b5a42] font-medium hover:underline">
|
||||
<button onClick={onBack} className="text-sm text-(--color-accent-dark) font-medium hover:underline">
|
||||
← Back to Report Cards
|
||||
</button>
|
||||
|
||||
<div className="bg-white rounded-2xl border border-stone-200 shadow-sm overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-[#f0ebe4] to-[#e8e0d5] p-6">
|
||||
<div className="bg-gradient-to-r from-(--color-accent-lighter) to-(--color-accent-light) p-6">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<h2 className="text-xl font-semibold text-stone-800">{card.petName}'s Grooming Report</h2>
|
||||
<button className="flex items-center gap-1.5 px-3 py-1.5 bg-white/80 text-stone-700 rounded-lg text-sm font-medium hover:bg-white">
|
||||
@@ -101,9 +101,9 @@ function ReportCardDetail({ card, onBack }: { card: ReportCard; onBack: () => vo
|
||||
</div>
|
||||
<p className="text-sm text-stone-600">{card.beforeDescription}</p>
|
||||
</div>
|
||||
<div className="rounded-xl bg-[#faf5ef] p-4">
|
||||
<p className="text-xs font-medium text-[#8b7355] uppercase mb-2">After</p>
|
||||
<div className="w-full h-32 bg-[#f0ebe4] rounded-lg flex items-center justify-center text-[#8b7355] text-sm mb-2">
|
||||
<div className="rounded-xl bg-(--color-accent-lighter) p-4">
|
||||
<p className="text-xs font-medium text-(--color-accent) uppercase mb-2">After</p>
|
||||
<div className="w-full h-32 bg-(--color-accent-light) rounded-lg flex items-center justify-center text-(--color-accent) text-sm mb-2">
|
||||
Photo placeholder
|
||||
</div>
|
||||
<p className="text-sm text-stone-700">{card.afterDescription}</p>
|
||||
@@ -148,7 +148,7 @@ function ReportCardDetail({ card, onBack }: { card: ReportCard; onBack: () => vo
|
||||
)}
|
||||
|
||||
{/* Groomer's Note */}
|
||||
<div className="bg-[#faf5ef] rounded-xl p-4">
|
||||
<div className="bg-(--color-accent-lighter) rounded-xl p-4">
|
||||
<h3 className="font-medium text-stone-800 mb-2">A Note from {card.groomerName}</h3>
|
||||
<p className="text-sm text-stone-700 italic leading-relaxed">"{card.groomerNote}"</p>
|
||||
</div>
|
||||
@@ -161,7 +161,7 @@ function ReportCardDetail({ card, onBack }: { card: ReportCard; onBack: () => vo
|
||||
{new Date(card.nextRecommendedDate).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" })}
|
||||
</p>
|
||||
</div>
|
||||
<button className="px-4 py-2 bg-[#8b7355] text-white rounded-lg text-sm font-medium hover:bg-[#7a6549]">
|
||||
<button className="px-4 py-2 bg-(--color-accent) text-white rounded-lg text-sm font-medium hover:bg-(--color-accent-hover)">
|
||||
Rebook Now
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user