48 lines
2.0 KiB
TypeScript
48 lines
2.0 KiB
TypeScript
import { authenticate } from "../shopify.server";
|
||
import { loadShopConfig } from "../services/config.server";
|
||
import { features } from "../services/plans";
|
||
|
||
/** Authenticate an embedded admin request and load the shop's config + plan features. */
|
||
export async function adminContext(request: Request, opts: { fresh?: boolean } = {}) {
|
||
const ctx = await authenticate.admin(request);
|
||
const cfg = await loadShopConfig(ctx.session.shop, opts);
|
||
return { ...ctx, cfg, features: features(cfg.shop.plan), actor: `merchant:${ctx.session.onlineAccessInfo?.associated_user?.email ?? "staff"}` };
|
||
}
|
||
|
||
export function formString(fd: FormData, k: string): string {
|
||
const v = fd.get(k);
|
||
return typeof v === "string" ? v.trim() : "";
|
||
}
|
||
export function formInt(fd: FormData, k: string): number | null {
|
||
const s = formString(fd, k);
|
||
if (s === "") return null;
|
||
const n = Number(s);
|
||
return Number.isFinite(n) ? Math.round(n) : null;
|
||
}
|
||
export function formMoney(fd: FormData, k: string): number | null {
|
||
const s = formString(fd, k);
|
||
if (s === "") return null;
|
||
const n = Number(s.replace(/[$,]/g, ""));
|
||
return Number.isFinite(n) ? Math.round(n * 100) : null;
|
||
}
|
||
export const formBool = (fd: FormData, k: string) => ["on", "true", "1"].includes(formString(fd, k));
|
||
export const formList = (fd: FormData, k: string) =>
|
||
formString(fd, k)
|
||
.split(/[,\n]/)
|
||
.map((s) => s.trim())
|
||
.filter(Boolean);
|
||
|
||
/** "10:00-13:00, 14:00-18:00" → ranges; throws on bad input. */
|
||
export function parseRanges(s: string): { start: string; end: string }[] {
|
||
if (!s.trim()) return [];
|
||
return s.split(",").map((part) => {
|
||
const m = part.trim().match(/^(\d{1,2}):(\d{2})\s*[-–]\s*(\d{1,2}):(\d{2})$/);
|
||
if (!m) throw new Error(`"${part.trim()}" isn't a time range like 10:00-13:00`);
|
||
const pad = (h: string) => h.padStart(2, "0");
|
||
const start = `${pad(m[1])}:${m[2]}`;
|
||
const end = `${pad(m[3])}:${m[4]}`;
|
||
if (start >= end) throw new Error(`${start}–${end}: end must be after start`);
|
||
return { start, end };
|
||
});
|
||
}
|