asset-management/src/api/client.js
vamsi.balaga f845af5125 feat: asset table view, file uploads, rich list columns and label cleanup
Assets
- Read assets from the asset table record view (de18263e-…), keyed by
  _pk_value; rows open by assetRowId and the list sorts newest first.
- Update INIT fields to the builder: Asset Details (was Category), Asset
  State/Priority/Classification options, warranty fields, Brand, ID,
  Location, and the images field (field_53); Asset Owner is field_51.
- Upload photos and an Invoice document before the activity runs, and
  send the returned file objects. The Invoice field id is a placeholder
  until the field exists in the builder.
- Show every Add Asset field as a list and details column, with option
  labels, and group related columns into rich cells.
- Use the INIT activity's detail view template (a573754f-…).
- Drop Assigned by, Condition and Type from the reassign, return and
  lost forms; add Lost manager name options.

Employees
- Department is a select from DEPARTMENTS; more employee types; status
  removed from the forms; rich columns in the employee list.

Shared
- apiFetch sends FormData bodies as multipart.
- Default workflow host is dev-apps.getzino.in.
- displayLabel() for readable field labels; Raw response panels removed.
- useRecordView accepts an initial sort.
- Temporary dev proxy in vite.config.js for rv-screens lookups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:32:19 +05:30

86 lines
2.3 KiB
JavaScript

/**
* Thin fetch wrapper for the Zino backend.
* Base URL comes from VITE_API_BASE_URL (see .env.example); falls back to dev.
*/
/** Auth host (login). */
export const API_BASE_URL = (
import.meta.env.VITE_API_BASE_URL || 'https://dev-apps.getzino.in'
).replace(/\/+$/, '')
/** App/workflow host (activities such as "start"). */
export const APP_API_BASE_URL = (
import.meta.env.VITE_APP_API_BASE_URL || 'https://dev-apps.getzino.in'
).replace(/\/+$/, '')
export const TOKEN_KEY = 'oam.auth.token'
export const USER_KEY = 'oam.auth.user'
export class ApiError extends Error {
constructor(message, { status, data } = {}) {
super(message)
this.name = 'ApiError'
this.status = status
this.data = data
}
}
export function getStoredToken() {
try {
return localStorage.getItem(TOKEN_KEY)
} catch {
return null
}
}
/**
* JSON request helper. Attaches the bearer token when one is stored,
* parses JSON responses and throws ApiError on non-2xx.
* A FormData body is sent as multipart; the browser sets its boundary.
*/
export async function apiFetch(path, { method = 'GET', body, headers, auth = true, ...rest } = {}) {
const url = path.startsWith('http') ? path : `${API_BASE_URL}${path}`
const token = auth ? getStoredToken() : null
const isForm = body instanceof FormData
let response
try {
response = await fetch(url, {
method,
headers: {
accept: 'application/json, text/plain, */*',
...(body !== undefined && !isForm && { 'content-type': 'application/json' }),
...(token && { authorization: `Bearer ${token}` }),
...headers,
},
body: isForm ? body : body !== undefined ? JSON.stringify(body) : undefined,
...rest,
})
} catch (err) {
throw new ApiError('Unable to reach the server. Check your connection and try again.', {
status: 0,
data: err,
})
}
const text = await response.text()
let data = null
if (text) {
try {
data = JSON.parse(text)
} catch {
data = text
}
}
if (!response.ok) {
const message =
(data && typeof data === 'object' && (data.error || data.message)) ||
(typeof data === 'string' && data) ||
`Request failed (${response.status})`
throw new ApiError(message, { status: response.status, data })
}
return data
}