Sms & Email
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// Day-end owner report settings: recipients, schedule, preview, test send and send log
|
||||
// (docs/DAY_END_OWNER_REPORT_PLAN.md §3.5). Singleton settings — no id; saves need the ETag.
|
||||
import { apiRequest, apiRequestWithETag, buildQuery } from "@/lib/api-client"
|
||||
import { ApiResult, PagedResponse } from "@/types/common"
|
||||
import {
|
||||
OwnerReportConfig,
|
||||
OwnerReportDispatch,
|
||||
OwnerReportPreview,
|
||||
OwnerReportSendResult,
|
||||
UpdateOwnerReportConfigRequest,
|
||||
} from "@/types/owner-report"
|
||||
|
||||
const BASE = "/settings/owner-report"
|
||||
|
||||
export const ownerReportApi = {
|
||||
get(): Promise<ApiResult<OwnerReportConfig>> {
|
||||
return apiRequestWithETag<OwnerReportConfig>(BASE)
|
||||
},
|
||||
|
||||
update(request: UpdateOwnerReportConfigRequest, ifMatch: string): Promise<ApiResult<OwnerReportConfig>> {
|
||||
return apiRequestWithETag<OwnerReportConfig>(BASE, { method: "PUT", body: request, ifMatch })
|
||||
},
|
||||
|
||||
/** The report as it would be sent for `date` (yyyy-MM-dd; default today) — nothing is sent. */
|
||||
preview(date?: string): Promise<OwnerReportPreview> {
|
||||
return apiRequest<OwnerReportPreview>(`${BASE}/preview${buildQuery({ date })}`)
|
||||
},
|
||||
|
||||
/** Sends the report now to every active recipient (logged as a test). One per minute. */
|
||||
sendTest(date?: string): Promise<OwnerReportSendResult[]> {
|
||||
return apiRequest<OwnerReportSendResult[]>(`${BASE}/test`, { method: "POST", body: { date: date || null } })
|
||||
},
|
||||
|
||||
dispatches(params: { page?: number; pageSize?: number } = {}): Promise<PagedResponse<OwnerReportDispatch>> {
|
||||
return apiRequest<PagedResponse<OwnerReportDispatch>>(`${BASE}/dispatches${buildQuery(params)}`)
|
||||
},
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
/**
|
||||
* Sri Lankan phone number rule shared by master-data forms (warehouses, …). Mirrors the
|
||||
* backend's `PhoneRules.Pattern` so the client rejects exactly what the API would.
|
||||
* Sri Lankan phone number rule shared by master-data forms (warehouses, owner report, …). Mirrors
|
||||
* the backend's `PhoneRules.Pattern` so the client rejects exactly what the API would.
|
||||
*
|
||||
* Accepted: 10 digits starting with 0 (0771234567, 011 234 5678) or +94 followed by 9 digits
|
||||
* (+94771234567, +94 77 123 4567). Spaces and hyphens are allowed between digits.
|
||||
* Accepted: 10 digits starting with 0 (0771234567, 011 234 5678), +94 followed by 9 digits
|
||||
* (+94771234567, +94 77 123 4567), or 94 followed by 9 digits without the "+" (94771234567 — the
|
||||
* SMS gateway's own format). Spaces and hyphens are allowed between digits.
|
||||
*/
|
||||
export const PHONE_PATTERN = /^(?:0(?:[\s-]*\d){9}|\+94(?:[\s-]*\d){9})[\s-]*$/
|
||||
export const PHONE_PATTERN = /^(?:0(?:[\s-]*\d){9}|\+?94(?:[\s-]*\d){9})[\s-]*$/
|
||||
|
||||
/** Digit count allowed after the prefix's own digits, per format. */
|
||||
const LOCAL_DIGITS = 10 // 0 + 9
|
||||
const INTL_DIGITS = 11 // 94 + 9
|
||||
|
||||
const PHONE_EXAMPLE = "e.g. 0771234567 or +94 77 123 4567"
|
||||
const PHONE_EXAMPLE = "e.g. 0771234567, 94771234567 or +94 77 123 4567"
|
||||
|
||||
/**
|
||||
* Input filter for a phone field's onChange: drops disallowed characters and stops accepting
|
||||
@@ -19,7 +20,7 @@ const PHONE_EXAMPLE = "e.g. 0771234567 or +94 77 123 4567"
|
||||
*/
|
||||
export function limitPhoneInput(raw: string): string {
|
||||
let out = ""
|
||||
let digits = 0
|
||||
let digits = ""
|
||||
for (const ch of raw) {
|
||||
if (ch === "+") {
|
||||
if (out.length === 0) out += ch // "+" only as the very first character
|
||||
@@ -30,10 +31,11 @@ export function limitPhoneInput(raw: string): string {
|
||||
continue
|
||||
}
|
||||
if (!/\d/.test(ch)) continue
|
||||
const max = out.startsWith("+") ? INTL_DIGITS : LOCAL_DIGITS
|
||||
if (digits >= max) continue
|
||||
// International (+94… or 94…) numbers carry two more digits than a local 0… number.
|
||||
const max = out.startsWith("+") || digits.startsWith("94") || (digits === "9" && ch === "4") ? INTL_DIGITS : LOCAL_DIGITS
|
||||
if (digits.length >= max) continue
|
||||
out += ch
|
||||
digits += 1
|
||||
digits += ch
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -44,11 +46,11 @@ export function phoneError(value: string, { required = false }: { required?: boo
|
||||
if (!phone) return required ? "Enter a phone number" : null
|
||||
if (/[^\d\s\-+]/.test(phone)) return "Phone number can only contain digits, spaces, - and a leading +"
|
||||
const digits = phone.replace(/\D/g, "")
|
||||
if (phone.startsWith("+")) {
|
||||
if (phone.startsWith("+") || digits.startsWith("94")) {
|
||||
if (!digits.startsWith("94")) return `International numbers must start with +94 — ${PHONE_EXAMPLE}`
|
||||
if (digits.length !== INTL_DIGITS) return `+94 numbers need exactly 9 digits after +94 (${digits.length - 2} entered)`
|
||||
if (digits.length !== INTL_DIGITS) return `94 numbers need exactly 9 digits after 94 (${digits.length - 2} entered)`
|
||||
} else {
|
||||
if (!digits.startsWith("0")) return `Phone number must start with 0 or +94 — ${PHONE_EXAMPLE}`
|
||||
if (!digits.startsWith("0")) return `Phone number must start with 0, 94 or +94 — ${PHONE_EXAMPLE}`
|
||||
if (digits.length !== LOCAL_DIGITS) return `Phone number must be exactly 10 digits (${digits.length} entered)`
|
||||
}
|
||||
if (!PHONE_PATTERN.test(phone)) return `Enter a valid phone number, ${PHONE_EXAMPLE}`
|
||||
|
||||
Reference in New Issue
Block a user