added empty select option
This commit is contained in:
parent
6f0e86007e
commit
2740961b7d
@ -360,6 +360,11 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
val = '';
|
val = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If select options are unselected/empty, explicitly send an empty string to the payload
|
||||||
|
if ((f.data_type === 'select' || f.data_type === 'multiselect' || f.data_type === 'wf_lookup' || f.data_type === 'radio') && val == null) {
|
||||||
|
val = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (val == null) continue;
|
if (val == null) continue;
|
||||||
|
|
||||||
if (f.data_type === 'phone' && typeof val === 'string') {
|
if (f.data_type === 'phone' && typeof val === 'string') {
|
||||||
@ -380,6 +385,17 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
const fileMeta = await client.uploadFile(val, { activityId: currentActivityId, fieldId: f.id, instanceId: currentInstanceId });
|
const fileMeta = await client.uploadFile(val, { activityId: currentActivityId, fieldId: f.id, instanceId: currentInstanceId });
|
||||||
payload[f.id] = [fileMeta];
|
payload[f.id] = [fileMeta];
|
||||||
} else {
|
} else {
|
||||||
|
if (f.data_type === 'grid' && Array.isArray(val)) {
|
||||||
|
val = val.map(row => {
|
||||||
|
const newRow = { ...row };
|
||||||
|
f.columns?.forEach(col => {
|
||||||
|
if ((col.data_type === 'select' || col.data_type === 'multiselect' || col.data_type === 'wf_lookup' || col.data_type === 'radio') && newRow[col.id] == null) {
|
||||||
|
newRow[col.id] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newRow;
|
||||||
|
});
|
||||||
|
}
|
||||||
payload[f.id] = val;
|
payload[f.id] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,36 @@ export interface RecordViewProps {
|
|||||||
initialFilters?: Record<string, string | string[]>;
|
initialFilters?: Record<string, string | string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to remove ugly UUIDs (with or without hyphens) from raw string values.
|
||||||
|
*/
|
||||||
|
function formatFilterLabel(raw: string): string {
|
||||||
|
if (!raw) return raw;
|
||||||
|
const stripped = raw.replace(/[_\-\s]*\(?(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{32})\)?/gi, '').trim();
|
||||||
|
return stripped || raw; // fallback to raw if the whole string was a UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a human-readable label for a field key, handling nested sub-columns and stripping UUIDs.
|
||||||
|
*/
|
||||||
|
function getFieldLabel(key: string, fields: RecordViewField[] = []): string {
|
||||||
|
const exactMatch = fields.find((f) => f.field_key === key);
|
||||||
|
if (exactMatch?.output_label) return exactMatch.output_label;
|
||||||
|
|
||||||
|
if (key.includes('.')) {
|
||||||
|
const [mainKey, subKey] = key.split('.', 2);
|
||||||
|
const mainField = fields.find((f) => f.field_key === mainKey);
|
||||||
|
if (mainField?.output_label) {
|
||||||
|
const subLabel = subKey.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
||||||
|
return `${mainField.output_label} (${subLabel})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Strip UUIDs and format the raw key
|
||||||
|
const noUuid = formatFilterLabel(key);
|
||||||
|
return noUuid.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()).trim();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic Zino record-view table for mobile. Fetches `POST /app/{id}/view/recordview`
|
* Generic Zino record-view table for mobile. Fetches `POST /app/{id}/view/recordview`
|
||||||
* (server-side paginated + searchable) and renders cards or table items.
|
* (server-side paginated + searchable) and renders cards or table items.
|
||||||
@ -232,15 +262,14 @@ export function RecordView({
|
|||||||
<div className="flex flex-wrap items-center gap-2 px-3 py-2 bg-slate-100/80 rounded-lg border border-border-subtle max-w-full overflow-hidden">
|
<div className="flex flex-wrap items-center gap-2 px-3 py-2 bg-slate-100/80 rounded-lg border border-border-subtle max-w-full overflow-hidden">
|
||||||
<span className="text-xs font-semibold text-slate-600 mr-0.5 shrink-0">Filters:</span>
|
<span className="text-xs font-semibold text-slate-600 mr-0.5 shrink-0">Filters:</span>
|
||||||
{Object.entries(activeFilters).flatMap(([key, vals]) => {
|
{Object.entries(activeFilters).flatMap(([key, vals]) => {
|
||||||
const fieldDef = resp?.config?.fields?.find((f) => f.field_key === key);
|
const label = getFieldLabel(key, resp?.config?.fields);
|
||||||
const label = fieldDef?.output_label || key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
||||||
return (vals || []).map((val) => (
|
return (vals || []).map((val) => (
|
||||||
<span
|
<span
|
||||||
key={`${key}-${val}`}
|
key={`${key}-${val}`}
|
||||||
className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium bg-navy-50 text-navy-900 border border-navy-200/80 shadow-xs max-w-full truncate"
|
className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium bg-navy-50 text-navy-900 border border-navy-200/80 shadow-xs max-w-full truncate"
|
||||||
>
|
>
|
||||||
<span className="font-bold text-navy-950 truncate max-w-[110px]">{label}:</span>
|
<span className="font-bold text-navy-950 truncate max-w-[110px]">{label}:</span>
|
||||||
<span className="truncate max-w-[130px]">{val}</span>
|
<span className="truncate max-w-[130px]">{formatFilterLabel(val)}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -301,8 +330,8 @@ export function RecordView({
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
{filterEntries.map((key) => {
|
{filterEntries.map((key) => {
|
||||||
const fieldDef = resp?.config.fields.find((f) => f.field_key === key);
|
const label = getFieldLabel(key, resp?.config?.fields);
|
||||||
const label = fieldDef?.output_label || key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
const fieldDef = resp?.config?.fields?.find((f) => f.field_key === key);
|
||||||
const isDateField =
|
const isDateField =
|
||||||
fieldDef?.data_type === 'date' ||
|
fieldDef?.data_type === 'date' ||
|
||||||
fieldDef?.data_type === 'datetime' ||
|
fieldDef?.data_type === 'datetime' ||
|
||||||
@ -331,7 +360,7 @@ export function RecordView({
|
|||||||
const options = resp?.config.filter_options?.[key] || [];
|
const options = resp?.config.filter_options?.[key] || [];
|
||||||
const opts = options.map((o) => ({
|
const opts = options.map((o) => ({
|
||||||
value: o,
|
value: o,
|
||||||
label: o.replace(/[_\-\s]*\(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\)?/i, '').trim() || o,
|
label: formatFilterLabel(o),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user