feat(GRO-1173): apply buffer rules UI changes to extracted groombook/web repo
This commit ports the GRO-1173 admin UI changes from the app monorepo into the extracted groombook/web repo, using the correct source paths (src/ instead of apps/web/src/): - New BufferRulesSection component (full CRUD UI for /api/buffer-rules) - Default Buffer (minutes) field added to service create/edit form - Size Category and Coat Type dropdowns added to PetForm (portal) - @groombook/types Service interface extended with defaultBufferMinutes - BufferRulesSection embedded in Settings page The PetForm already had coatType — this commit adds petSizeCategory and renders both fields with proper dropdown selectors. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
+19
-1
@@ -6,6 +6,7 @@ interface ServiceForm {
|
||||
description: string;
|
||||
priceStr: string;
|
||||
durationMinutes: number;
|
||||
defaultBufferMinutes: number;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
@@ -14,6 +15,7 @@ const EMPTY_FORM: ServiceForm = {
|
||||
description: "",
|
||||
priceStr: "",
|
||||
durationMinutes: 60,
|
||||
defaultBufferMinutes: 0,
|
||||
active: true,
|
||||
};
|
||||
|
||||
@@ -55,6 +57,7 @@ export function ServicesPage() {
|
||||
description: s.description ?? "",
|
||||
priceStr: (s.basePriceCents / 100).toFixed(2),
|
||||
durationMinutes: s.durationMinutes,
|
||||
defaultBufferMinutes: s.defaultBufferMinutes ?? 0,
|
||||
active: s.active,
|
||||
});
|
||||
setFormError(null);
|
||||
@@ -76,6 +79,7 @@ export function ServicesPage() {
|
||||
description: form.description || undefined,
|
||||
basePriceCents: Math.round(price * 100),
|
||||
durationMinutes: form.durationMinutes,
|
||||
defaultBufferMinutes: form.defaultBufferMinutes,
|
||||
active: form.active,
|
||||
};
|
||||
const res = editing
|
||||
@@ -138,7 +142,7 @@ export function ServicesPage() {
|
||||
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 14 }}>
|
||||
<thead>
|
||||
<tr style={{ background: "#f8fafc" }}>
|
||||
{["Name", "Description", "Price", "Duration", "Status", ""].map((h) => (
|
||||
{["Name", "Description", "Price", "Duration", "Default Buffer", "Status", ""].map((h) => (
|
||||
<th key={h} style={{ textAlign: "left", padding: "0.55rem 0.75rem", borderBottom: "1px solid #e5e7eb", fontSize: 11, fontWeight: 600, color: "#6b7280", textTransform: "uppercase", letterSpacing: "0.04em" }}>
|
||||
{h}
|
||||
</th>
|
||||
@@ -152,6 +156,7 @@ export function ServicesPage() {
|
||||
<td style={tdStyle}>{s.description ?? "—"}</td>
|
||||
<td style={tdStyle}>${(s.basePriceCents / 100).toFixed(2)}</td>
|
||||
<td style={tdStyle}>{s.durationMinutes} min</td>
|
||||
<td style={tdStyle}>{(s as Service & { defaultBufferMinutes?: number }).defaultBufferMinutes ?? 0} min</td>
|
||||
<td style={tdStyle}>
|
||||
<button
|
||||
onClick={() => toggleActive(s)}
|
||||
@@ -240,6 +245,19 @@ export function ServicesPage() {
|
||||
style={inputStyle}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Default Buffer (minutes)">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
value={form.defaultBufferMinutes}
|
||||
onChange={(e) => setForm((f) => ({ ...f, defaultBufferMinutes: parseInt(e.target.value) || 0 }))}
|
||||
style={inputStyle}
|
||||
/>
|
||||
<p style={{ fontSize: 12, color: "#9ca3af", marginTop: "0.2rem" }}>
|
||||
Default buffer time applied when no specific rule matches
|
||||
</p>
|
||||
</Field>
|
||||
<Field label="Status">
|
||||
<label style={{ display: "flex", alignItems: "center", gap: "0.5rem", cursor: "pointer" }}>
|
||||
<input
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useBranding } from "../BrandingContext.js";
|
||||
import { BufferRulesSection } from "../components/BufferRules.js";
|
||||
|
||||
interface AuthProviderConfig {
|
||||
id: number;
|
||||
@@ -533,6 +534,10 @@ issuerUrl: authForm.issuerUrl,
|
||||
{saving ? "Saving..." : "Save Changes"}
|
||||
</button>
|
||||
|
||||
{/* Buffer Rules Section */}
|
||||
<hr style={{ margin: "2rem 0", border: "none", borderTop: "1px solid #e5e7eb" }} />
|
||||
<BufferRulesSection />
|
||||
|
||||
{/* Auth Provider Section — super users only */}
|
||||
{currentUser?.isSuperUser && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user