Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0019511061 | |||
| 9a0a63d1df | |||
| 24a032dd9d | |||
| 13f2550ee2 | |||
| f29ac2e40d | |||
| 25dae6af58 | |||
| 4737fc9dd8 | |||
| bdcad0d9dc | |||
| 6da19d51fc | |||
| c919632aea | |||
| b1124e6a6c | |||
| 90794e4e14 | |||
| 522e5dbf63 | |||
| 39f603589b | |||
| 2ab06853e6 | |||
| e7fd820b31 | |||
| fafb717e5a |
@@ -1,7 +1,6 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { genericOAuth } from "better-auth/plugins";
|
||||
import { google, github } from "better-auth/social-providers";
|
||||
import { getDb, authProviderConfig, eq } from "@groombook/db";
|
||||
import { decryptSecret } from "@groombook/db";
|
||||
|
||||
@@ -171,19 +170,7 @@ export async function initAuth(): Promise<void> {
|
||||
const hasGoogle = !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET);
|
||||
const hasGitHub = !!(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET);
|
||||
|
||||
const socialPlugins = [];
|
||||
if (hasGoogle) {
|
||||
socialPlugins.push(google({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
}));
|
||||
}
|
||||
if (hasGitHub) {
|
||||
socialPlugins.push(github({
|
||||
clientId: process.env.GITHUB_CLIENT_ID!,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
||||
}));
|
||||
}
|
||||
const callbackBase = `${BETTER_AUTH_URL}/api/auth/callback`;
|
||||
|
||||
// Build Better-Auth instance using resolved config
|
||||
authInstance = betterAuth({
|
||||
@@ -212,8 +199,23 @@ export async function initAuth(): Promise<void> {
|
||||
},
|
||||
],
|
||||
}),
|
||||
...socialPlugins,
|
||||
],
|
||||
socialProviders: {
|
||||
...(hasGoogle ? {
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
redirectURI: `${callbackBase}/google`,
|
||||
},
|
||||
} : {}),
|
||||
...(hasGitHub ? {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID!,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
||||
redirectURI: `${callbackBase}/github`,
|
||||
},
|
||||
} : {}),
|
||||
},
|
||||
session: {
|
||||
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
||||
updateAge: 60 * 60 * 24, // 1 day
|
||||
|
||||
@@ -44,7 +44,10 @@ test.beforeEach(async ({ page }) => {
|
||||
json: { newClients: [], activeInPeriodCount: 0, churnRisk: [], churnRiskTotal: 0 },
|
||||
});
|
||||
}
|
||||
// Appointments, clients, services, staff, invoices, book, etc.
|
||||
if (url.includes("/api/invoices")) {
|
||||
return route.fulfill({ json: { data: [], total: 0 } });
|
||||
}
|
||||
// Appointments, clients, services, staff, book, etc.
|
||||
return route.fulfill({ json: [] });
|
||||
});
|
||||
});
|
||||
@@ -82,6 +85,7 @@ test("admin staff page loads", async ({ page }) => {
|
||||
|
||||
test("admin invoices page loads", async ({ page }) => {
|
||||
await page.goto("/admin/invoices");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
await expect(page.getByText("GroomBook")).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "Invoices" })).toBeVisible();
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 184 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96C853FAECD363909C4A0</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 227 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96CFC84D7A9333708F278</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96D48D7892E37386B9ACB</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 196 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96C25663D703833F23607</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96D89851C843332073968</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 205 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96C9C5A03D33730C61AD8</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 307 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 252 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96BEB91911B30317E3BE8</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 199 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96BFB7B92D33535D6D90D</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96B8BDF4B473630A2E120</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Error>
|
||||
<Code>AccessDenied</Code>
|
||||
<Message>You have no right to access this object because of bucket acl.</Message>
|
||||
<RequestId>69D96D78BFFCAD343037C27C</RequestId>
|
||||
<HostId>hailuo-image-algeng-data-us.oss-us-east-1.aliyuncs.com</HostId>
|
||||
<EC>0003-00000001</EC>
|
||||
<RecommendDoc>https://api.alibabacloud.com/troubleshoot?q=0003-00000001</RecommendDoc>
|
||||
</Error>
|
||||
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 279 KiB |
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import requests
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
api_key = os.environ.get("MINIMAX_API_KEY")
|
||||
if not api_key:
|
||||
raise ValueError("MINIMAX_API_KEY environment variable not set")
|
||||
|
||||
url = "https://api.minimax.io/v1/image_generation"
|
||||
headers = {"Authorization": f"Bearer {api_key}"}
|
||||
|
||||
os.makedirs("minimax-output", exist_ok=True)
|
||||
|
||||
# Comprehensive list of dog breeds and variations for diverse demo data
|
||||
dog_prompts = [
|
||||
# Large breeds
|
||||
("german-shepherd-alert", "German Shepherd dog with alert expression, standing confidently, professional pet photography, studio lighting, photorealistic"),
|
||||
("golden-retriever-happy", "Golden Retriever with joyful expression, sitting, golden coat, natural daylight, professional pet photography, photorealistic"),
|
||||
("labrador-running", "Black Labrador Retriever running towards camera, outdoor park setting, dynamic pose, professional pet photography, photorealistic"),
|
||||
("german-shepherd-sitting", "German Shepherd sitting in front of studio backdrop, professional portrait, studio lighting, photorealistic"),
|
||||
("golden-retriever-lying", "Golden Retriever lying down on grass, peaceful expression, outdoor natural lighting, professional pet photography, photorealistic"),
|
||||
|
||||
# Medium breeds
|
||||
("beagle-curious", "Beagle with curious expression, sitting, outdoor garden setting, professional pet photography, photorealistic"),
|
||||
("cocker-spaniel-groomed", "Cocker Spaniel freshly groomed with fluffy coat, happy expression, professional grooming studio, photorealistic"),
|
||||
("english-springer-spaniel", "English Springer Spaniel in natural outdoor setting, alert pose, professional pet photography, photorealistic"),
|
||||
("boxer-playful", "Boxer dog with playful expression, standing, muscular build, professional studio lighting, photorealistic"),
|
||||
("bulldog-gentle", "English Bulldog with gentle expression, sitting, studio backdrop, professional pet photography, photorealistic"),
|
||||
|
||||
# Small breeds
|
||||
("maltese-fluffy", "Maltese dog with white fluffy coat, sitting, groomed appearance, professional pet photography, studio lighting, photorealistic"),
|
||||
("shih-tzu-groomed", "Shih Tzu with long groomed coat, sitting pretty, professional grooming studio, photorealistic"),
|
||||
("pomeranian-alert", "Pomeranian with alert expression, standing, fluffy coat, professional pet photography, photorealistic"),
|
||||
("yorkshire-terrier", "Yorkshire Terrier with silky coat, sitting, professional grooming environment, photorealistic"),
|
||||
("pug-curious", "Pug with curious expression, sitting, studio lighting, professional pet photography, photorealistic"),
|
||||
|
||||
# Specialty breeds
|
||||
("poodle-standard-groomed", "Standard Poodle with professionally groomed coat, standing in show stance, professional grooming studio, photorealistic"),
|
||||
("dachshund-long", "Long-haired Dachshund, lying down, relaxed pose, professional pet photography, photorealistic"),
|
||||
("corgi-happy", "Welsh Corgi with happy expression, standing, professional outdoor setting, photorealistic"),
|
||||
("husky-alert", "Siberian Husky with alert expression, sitting, professional pet photography, studio lighting, photorealistic"),
|
||||
("german-shepherd-lying", "German Shepherd lying down in relaxed pose, indoor setting, professional pet photography, photorealistic"),
|
||||
|
||||
# Mixed/rescue variations
|
||||
("mixed-breed-brown", "Brown and white mixed breed dog, friendly expression, sitting, professional pet photography, photorealistic"),
|
||||
("mixed-breed-black", "Black mixed breed dog with gentle eyes, standing, outdoor natural lighting, photorealistic"),
|
||||
("mixed-breed-spotted", "Spotted mixed breed dog, playful pose, outdoor park setting, professional pet photography, photorealistic"),
|
||||
("terrier-mix-sitting", "Terrier mix dog, alert expression, sitting, professional studio backdrop, photorealistic"),
|
||||
("spaniel-mix-outdoor", "Spaniel mix dog in outdoor garden, relaxed pose, natural daylight, professional pet photography, photorealistic"),
|
||||
|
||||
# Additional variations
|
||||
("labrador-golden", "Golden Labrador Retriever, calm expression, standing in professional pose, studio lighting, photorealistic"),
|
||||
("labrador-black-sitting", "Black Labrador Retriever sitting, gentle expression, professional pet photography, photorealistic"),
|
||||
("rottweiler-calm", "Rottweiler with calm expression, sitting, professional studio, photorealistic"),
|
||||
("doberman-alert", "Doberman Pinscher with alert expression, standing, professional pet photography, photorealistic"),
|
||||
("german-shepherd-side", "German Shepherd in side profile, standing, professional outdoor setting, photorealistic"),
|
||||
]
|
||||
|
||||
print(f"Generating {len(dog_prompts)} unique dog images...")
|
||||
print(f"Start time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("")
|
||||
|
||||
generated = 0
|
||||
failed = 0
|
||||
|
||||
for i, (filename_base, prompt) in enumerate(dog_prompts, 1):
|
||||
filename = f"dog-{filename_base}.png"
|
||||
filepath = f"minimax-output/{filename}"
|
||||
|
||||
# Check if already exists
|
||||
if os.path.exists(filepath):
|
||||
size = os.path.getsize(filepath)
|
||||
print(f"[{i:2d}/{len(dog_prompts)}] ✓ {filename} (already exists, {size} bytes)")
|
||||
generated += 1
|
||||
continue
|
||||
|
||||
print(f"[{i:2d}/{len(dog_prompts)}] Generating {filename}...", end=" ", flush=True)
|
||||
|
||||
payload = {
|
||||
"model": "image-01",
|
||||
"prompt": prompt,
|
||||
"aspect_ratio": "1:1",
|
||||
"response_format": "base64",
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=120)
|
||||
|
||||
# Check for quota errors
|
||||
if response.status_code == 429:
|
||||
print(f"✗ QUOTA EXCEEDED")
|
||||
print(f"\nQuota limit reached after {generated} successful generations")
|
||||
break
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
if "data" in data and "image_base64" in data["data"]:
|
||||
images = data["data"]["image_base64"]
|
||||
|
||||
with open(filepath, "wb") as f:
|
||||
f.write(base64.b64decode(images[0]))
|
||||
|
||||
file_size = os.path.getsize(filepath)
|
||||
print(f"✓ ({file_size} bytes)")
|
||||
generated += 1
|
||||
else:
|
||||
print(f"✗ Unexpected response format")
|
||||
failed += 1
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"✗ Timeout")
|
||||
failed += 1
|
||||
except requests.exceptions.RequestException as e:
|
||||
if "429" in str(e) or "quota" in str(e).lower():
|
||||
print(f"✗ QUOTA EXCEEDED")
|
||||
print(f"\nQuota limit reached after {generated} successful generations")
|
||||
break
|
||||
else:
|
||||
print(f"✗ {type(e).__name__}")
|
||||
failed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ {type(e).__name__}")
|
||||
failed += 1
|
||||
|
||||
time.sleep(0.5) # Small delay between requests
|
||||
|
||||
print("")
|
||||
print(f"End time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"✓ Successfully generated: {generated}")
|
||||
print(f"✗ Failed: {failed}")
|
||||
print(f"\nCopying images to demo-pets directory...")
|
||||
|
||||
# Copy all generated images to demo-pets
|
||||
import subprocess
|
||||
result = subprocess.run(
|
||||
["cp", "-v", "minimax-output/dog-*.png", "apps/web/public/demo-pets/"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Count files in demo-pets
|
||||
import glob
|
||||
demo_pets = glob.glob("apps/web/public/demo-pets/dog-*.png")
|
||||
print(f"✓ Copied to demo-pets. Total dog images: {len(demo_pets)}")
|
||||
else:
|
||||
print(f"Note: Copy result - {result.stderr}")
|
||||
@@ -184,7 +184,7 @@ const dogBreeds = [
|
||||
"Brittany", "English Springer Spaniel", "Maltese", "Bichon Frise",
|
||||
"West Highland White Terrier", "Vizsla", "Chihuahua", "Collie",
|
||||
"Basset Hound", "Newfoundland", "Samoyed", "Australian Shepherd",
|
||||
"Pembroke Welsh Corgi", "French Bulldog", "Weimaraner",
|
||||
"Pembroke Welsh Corgi", "French Bulldog", "Weimaraner", "Puggle",
|
||||
"Mixed Breed", "Mixed Breed", "Mixed Breed",
|
||||
];
|
||||
|
||||
@@ -281,6 +281,44 @@ const productsUsed = [
|
||||
"Coconut oil shampoo, leave-in conditioner, cologne",
|
||||
];
|
||||
|
||||
const demoPetImages = [
|
||||
"/demo-pets/dog-golden-after.png",
|
||||
"/demo-pets/dog-poodle-groomed.png",
|
||||
"/demo-pets/dog-black-lab.png",
|
||||
"/demo-pets/dog-shih-tzu.png",
|
||||
"/demo-pets/dog-cocker-spaniel.png",
|
||||
"/demo-pets/dog-schnauzer.png",
|
||||
"/demo-pets/dog-maltese.png",
|
||||
"/demo-pets/dog-dachshund.png",
|
||||
"/demo-pets/dog-pomeranian.png",
|
||||
"/demo-pets/dog-bichon-frise.png",
|
||||
"/demo-pets/dog-golden-retriever.png",
|
||||
"/demo-pets/dog-labrador.png",
|
||||
"/demo-pets/dog-mixed-breed.png",
|
||||
"/demo-pets/dog-poodle.png",
|
||||
"/demo-pets/dog-terrier.png",
|
||||
"/demo-pets/dog-afghan-hound.png",
|
||||
"/demo-pets/dog-basset-brown-white.png",
|
||||
"/demo-pets/dog-bichon-white-groomed.png",
|
||||
"/demo-pets/dog-boxer-fawn-athletic.png",
|
||||
"/demo-pets/dog-cavalier-cream-gentle.png",
|
||||
"/demo-pets/dog-cocker-buff-friendly.png",
|
||||
"/demo-pets/dog-corgi.png",
|
||||
"/demo-pets/dog-dachshund-black-tan.png",
|
||||
"/demo-pets/dog-golden-before.png",
|
||||
"/demo-pets/dog-pomeranian-white-studio.png",
|
||||
"/demo-pets/dog-schnauzer-black-groomed.png",
|
||||
"/demo-pets/dog-setter-red-sunlit.png",
|
||||
"/demo-pets/dog-sheepdog-merle-running.png",
|
||||
];
|
||||
|
||||
const puggleImages = [
|
||||
"/demo-pets/dog-puggle-fawn-playful.png",
|
||||
"/demo-pets/dog-puggle-black-sitting.png",
|
||||
"/demo-pets/dog-puggle-cream-groomed.png",
|
||||
"/demo-pets/dog-puggle-fawn-grooming.png",
|
||||
];
|
||||
|
||||
// ── Service definitions ──────────────────────────────────────────────────────
|
||||
// Deterministic service IDs + UNIQUE(name) constraint make seed fully idempotent:
|
||||
// first run inserts, subsequent runs update existing rows via ON CONFLICT (name).
|
||||
@@ -621,6 +659,7 @@ async function seed() {
|
||||
const clientRecords: ClientRecord[] = [];
|
||||
const petRecords: PetRecord[] = [];
|
||||
|
||||
let petIndex = 0; // Track pet count to assign Puggle images to first 250 pets
|
||||
const clientBatchSize = 50;
|
||||
for (let batch = 0; batch < Math.ceil(cfg.clientCount / clientBatchSize); batch++) {
|
||||
const clientBatch: (typeof schema.clients.$inferInsert)[] = [];
|
||||
@@ -652,7 +691,7 @@ async function seed() {
|
||||
const petCount = rand() < 0.5 ? 1 : rand() < 0.7 ? 2 : 3;
|
||||
for (let p = 0; p < petCount; p++) {
|
||||
const petId = uuid();
|
||||
const breed = pick(dogBreeds);
|
||||
const breed = petIndex < 250 ? "Puggle" : pick(dogBreeds);
|
||||
const dob = new Date(now);
|
||||
dob.setFullYear(dob.getFullYear() - randInt(1, 14));
|
||||
dob.setMonth(randInt(0, 11));
|
||||
@@ -671,9 +710,11 @@ async function seed() {
|
||||
shampooPreference: pick(shampoos),
|
||||
specialCareNotes: rand() < 0.1 ? "Vet clearance required before grooming" : null,
|
||||
customFields: {},
|
||||
image: petIndex < 250 ? pick(puggleImages) : pick(demoPetImages),
|
||||
});
|
||||
|
||||
petRecords.push({ id: petId, clientId });
|
||||
petIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,6 +745,7 @@ async function seed() {
|
||||
shampooPreference: pet.shampooPreference,
|
||||
specialCareNotes: pet.specialCareNotes,
|
||||
customFields: pet.customFields,
|
||||
image: pet.image,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -738,8 +780,8 @@ async function seed() {
|
||||
.values({ id: uc.id, name: uc.name, email: uc.email, phone: uc.phone, address: uc.address })
|
||||
.onConflictDoUpdate({ target: schema.clients.id, set: { name: uc.name, email: uc.email, phone: uc.phone, address: uc.address } });
|
||||
await db.insert(schema.pets)
|
||||
.values({ id: uc.petId, clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z") })
|
||||
.onConflictDoUpdate({ target: schema.pets.id, set: { clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z") } });
|
||||
.values({ id: uc.petId, clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z"), image: pick(demoPetImages) })
|
||||
.onConflictDoUpdate({ target: schema.pets.id, set: { clientId: uc.id, name: uc.petName, species: "Dog", breed: uc.petBreed, weightKg: "25.00", dateOfBirth: new Date("2021-03-15T00:00:00Z"), image: pick(demoPetImages) } });
|
||||
// Create one completed appointment for this client
|
||||
const apptId = uuid();
|
||||
const svcIdx = 0;
|
||||
|
||||