feat: add document sharing functionality across various modules

- Introduced ShareDocumentButton component for sharing documents.
- Added ShareDocumentDialog for email and WhatsApp sharing options.
- Integrated document sharing in estimates, invoices, inspections, job cards, bills, and purchase orders.
- Implemented useDocumentShare hook for handling share logic.
- Created DocumentShareClient for API interactions related to document sharing.
- Updated layouts and actions to include sharing options for relevant entities.
This commit is contained in:
2026-05-14 12:21:01 +04:00
parent 05b55b5721
commit fcbba6247d
22 changed files with 571 additions and 60 deletions
@@ -0,0 +1,63 @@
import { ApiClient, ApiError, type ApiClientOptions } from "../infra/client"
import type { ApiPath } from "../infra/types"
export const DOCUMENT_SHARE_ROUTES = {
STORE: "/api/documents/share",
} as const satisfies Record<string, ApiPath>
export type DocumentShareType =
| "inspection"
| "estimate"
| "job_card"
| "invoice"
| "payment_received"
| "purchase_order"
| "bill"
| string
export type DocumentShareChannel = "email" | "link"
export interface DocumentSharePayload {
document_type: DocumentShareType
document_id: number | string
channel: DocumentShareChannel
email?: string
phone?: string
message?: string
}
export interface DocumentShareResponse {
share_url: string
whatsapp_url: string
expires_at: string | null
}
export class DocumentShareClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
}
async share(payload: DocumentSharePayload): Promise<DocumentShareResponse> {
const endpoint = DOCUMENT_SHARE_ROUTES.STORE
const url = `${this.baseUrl.replace(/\/+$/, "")}${endpoint}`
const headers = new Headers(this.defaultOptions.headers as Record<string, string>)
headers.set("Accept", "application/json")
headers.set("Content-Type", "application/json")
const body = { ...payload, document_id: Number(payload.document_id) }
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
})
const text = await response.text()
const data = text ? JSON.parse(text) : null
if (!response.ok) {
throw new ApiError(response.status, response.statusText, endpoint, "post", data)
}
return data as DocumentShareResponse
}
}
+8
View File
@@ -46,6 +46,14 @@ export {
export { BillsClient, BILL_ROUTES } from "./bills"
export { ReasonsClient, REASON_ROUTES } from "./reasons"
export { DocumentPrintClient, DOCUMENT_PRINT_ROUTES, type DocumentPrintType, type DocumentPrintMode } from "./document-print"
export {
DocumentShareClient,
DOCUMENT_SHARE_ROUTES,
type DocumentShareType,
type DocumentShareChannel,
type DocumentSharePayload,
type DocumentShareResponse,
} from "./document-share"
export { HolidaysClient, HOLIDAY_ROUTES } from "./holidays"
export { MakeAndModelsClient, MAKE_AND_MODEL_ROUTES } from "./make-and-models"
export { TimeSheetsClient, TIME_SHEET_ROUTES } from "./time-sheets"